Saturday, March 20, 2010

Building a Windows Phone 7 app with MVVM pattern, using TDD and mock objects

 
If you've worked through the long form already, try the short form (instructions only).

*****

Source code for this kata published to github.

The goal of this article is to take you through the process of creating a Windows Phone 7 app using Test-Driven Development, designing each feature of the app through a series of incremental tests. The concept is loosely modeled on the TDD Calculator kata as explained by Roy Osherove, although introducing an entire framework, with as many steps as required in the instructions below, moves outside the boundaries of a properly focused kata to what might more properly be called a tutorial. However, my goal is that these steps could be repeatedly practised, like a kata, to develop a consistent approach to TDD using the Windows Phone 7 unit testing framework.

To be able to test effectively in the presentation layer, we want to use the appropriate pattern. The pattern for XAML frameworks (WPF, Silverlight, and now Windows Phone 7) is the Model-View-ViewModel (MVVM) design pattern, in which the ViewModel is isolated from the presentation layer and therefore testable (much like the controller in the MVC pattern, or presenter in the MVP pattern.) The difference here is that the passing of data back and forth between ViewModel and View is done by the framework itself, using a data binding implementation. (This will get much clearer as you work through the tests.)

The initial tests will build the ViewModel itself, ensuring that it has all the pieces its need (implemented interfaces, constructor parameters, and so on) to perform its role in the MVVM pattern. Items not directly part of the SUT (system under test) will be mocked in.

NOTE Like many presentation layer patterns, we will want to use mock objects to substitute classes that are not directly part of the system under test (in this case, the ViewModel.) However, Windows Phone 7 runs on the .NET Compact Framework, which does not support full mocking frameworks such as Rhino Mocks and Moq. Therefore, in this kata we will create fakes (custom mock classes) to verify the repository queries have been called. As per the terminology used by Gerard Meszaros (see Mocks, Fakes, Stubs and Dummies) perhaps the best term to describe what we are building for our custom mock is a "Test Spy".

So let's get started.

Pre-Requisite: Install Windows Phone 7 Unit Testing Framework
To unit test a Windows Phone 7 application, as pre-requisite you must install and configure the Windows Phone 7-specific version of the Silverlight unit testing framework (in which test results are displayed directly in the Winodws Phone emulator screen.) NUnit and other xUnit testing frameworks are not currently compatible with Silverlight and Windows Phone 7.

To install:
1) Download/install Visual Studio 2010 Express for Windows Phone from here.
2) Download the zip file containing the 2 unsigned DLLs from Jeff Wilcox's website.
3) Unzip the 2 DLLs. Then, right-click each DLL individually and select "Properties".
4) In the Properties window for each DLL, click the "Unblock" button. (DLLs off the net are blocked for safety by default.)
5) Launch Visual Studio 2010 Express for Windows Phone.
6) Create a new solution where the project type is "Windows Phone Application"
7) In the Windows Phone Application project, add References for the 2 DLLs that you have just unblocked to this Windows project:
  • Microsoft.Silverlight.Testing
  • Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight
8) DON'T create any separate class library projects. For now, the unit testing framework requires that all tests/supporting classes be INSIDE the Windows project.
9) DO create a sub-folder in the Windows project, and give it a name to imply this is the application code (it will display in the namespace path). (examples: "AppCode" or "Support")
10) Finally, for the test framework to recognize your tests, you must add code to the code-behind class for the MainPage XAML file.
11) Open the file MainPage.xaml.cs
12) Add using statements to the Silverlight Unit Testing Framework DLLs you referenced earlier:

using Microsoft.Phone.Controls;
using Microsoft.Silverlight.Testing;

13) FINALLY, add the following lines of code (thanks to Jeff Wilcox for this) at the end of the constructor, just below the SupportedOrientations assignment. Note that we have declared a boolean constant (runUnitTests) which we've assigned to true, and then wrapped the code which initializes the unit testing framework into a conditional block based on this constant. With this constant set to true, the Windows Phone emulator will display the test results from the test runner. Later, when we want to view the phone app itself, we'll be setting this constant to false.

const bool runUnitTests = true;

if (runUnitTests)
{
    Content = UnitTestSystem.CreateTestPage();
    IMobileTestPage imtp = Content as IMobileTestPage;

    if (imtp != null)
    {
        BackKeyPress += (x, xe) => xe.Cancel = imtp.NavigateBack();
    }
}

14) Proceed to the TDD kata.

TDD Kata Setup
Within the application code sub-folder (created in the pre-requisites above), create the following sub-folders to function as namespaces: Domain, Repository, ViewModel, and Tests.Unit.

When completed, your SolutionExplorer should look like this:


Test #1 (ViewModel injects corresponding domain entity)
1) In the Tests.Unit sub-folder, create CustomerViewModelTests.cs class.
2) Add the following using statements:

using Microsoft.Silverlight.Testing.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;

3) Add the [TestClass] attribute to class. (Silverlight Unit Testing Framework is built on top of the MSTest framework, hence the naming convention for the test attributes is the same.)
4) The first test will requite a customer repository, so declare a private instance variable of non-existent type ICustomerRepository.

private ICustomerRepository _customerRepository;

5) Use the Visual Studio 2010 Generate from Usage feature to create the interface:
  • click into the type name, press Alt-Shift-F10
  • select "Generate new type...'"
  • In the Generate New Type dialog (see above), be sure to change the definition to interface, and then click OK.
  • In SolutionExplorer, select the ICustomerRepository class that has been generated in the Tests.Unit folder and use cut-and-paste to MOVE it into the Repository folder
  • In the Repository folder, double-click to open ICustomerRepository and fix its namespace declaration to be for the Repository folder.
  • Return to the test class; where the type reference is now broken
  • click on the type ICustomerRepository and press Alt-Shift-F10 to add a proper reference to the Repository namespace.
6) Create a test setup method.

[TestInitialize]
public void SetUp()
{
}

7) In the setup method, instantiate the _customerRepository interface as a new class, FakeCustomerRepository. Use Alt-Shft-F10 (Generate By Usage) to create this class. In the class signature, add ICustomerRepository as its implemented interface. Now move the class (cut-and-paste it within SolutionExplorer to move it to the Repository folder. Don't forget to edit its namespace entry to match the Repository.)

[TestInitialize]
public void SetUp()
{
    _customerRepository = new FakeCustomerRepository();
}

8) Create a test for CustomerViewModel, in which _customerRepository and a new instance of Customer are passed as parameters to the constructor. Since Customer is a new class; use Alt-Shft-F10 (Generate By Usage) to create the class, and to initialize FirstName and LastName properties. In Solution Explorer, cut-and-paste to move this class to the Domain sub-folder, then edit its namespace declaration to match.

[TestMethod]
public void Constructor_CustomerAndRepositoryInput_InstantiatesSuccessfully()
{
    var customer = new Customer { FirstName = "June", LastName = "Wong" };
    var sut = new CustomerViewModel(_customerRepository, customer);
    Assert.IsNotNull(sut);
}

NOTE It is common in tests to keep track of which class is the "System Under Test" by naming the class variable "sut". The supplied code snippets will use this convention.

9) You are ready for your first test run using the Windows Phone 7 unit test framework. Right-click on the solution, and click "Deploy Solution". The test should pass, displaying as follows:
NOTE The items listed in the test results can be drilled-down into for more detail. Click the item to drill-down, click the back arrow icon at the bottom of the phone emulator to go back.

Test #2 (ViewModel property values match domain property values)
10) The correct implementation of the MVVM pattern is the ViewModel "wraps" the model (i.e the domain.) Therefore a domain entity, Customer, should have it's properties exposed, but not replicated, through the ViewModel. Therefore, create test for CustomerViewModel which verifies that:
    * Customer.FirstName is wrapped by CustomerViewModel.FirstName
    * Customer.LastName is wrapped by CustomerViewModel.LastName

[TestMethod]
public void Constructor_CustomerAndRepositoryInput_CustomerPropertiesEqualViewModelProperties()
{
    var customer = new Customer { FirstName = "June", LastName = "Wong" };
    var sut = new CustomerViewModel(_customerRepository, customer);

    Assert.AreEqual(customer.FirstName, sut.FirstName);
    Assert.AreEqual(customer.LastName, sut.LastName);
}

CustomerViewModel properties

public string FirstName
{
    get
    {
        return _customer.FirstName;
    }
    set
    {
        _customer.FirstName = value;
    }
}

public string LastName
{
    get
    {
        return _customer.LastName;
    }
    set
    {
        _customer.LastName = value;
    }
}

Test #3 (ViewModel.VerifyPropertyName(string property))
11) Create test for CustomerViewModel which verifies that the VerifyPropertyName(string property) throws a custom exception if the property is not part of the model.

[TestMethod]
[ExpectedException(typeof(VerifyPropertyNameException))]
public void VerifyPropertyNameMethod_NonExistentPropertyString_ThrowsException()
{
    var customer = new Customer() { FirstName = "June", LastName = "Smith" };
    var sut = new CustomerViewModel(_customerRepository, customer);
    sut.VerifyPropertyName("NonExistentPropertyName");
}

corresponding method

public void VerifyPropertyName(string propertyName)
{
    foreach(var propertyInfo in this.GetType().GetProperties())
    {
        if(propertyName == propertyInfo.Name)
        {
            return;
        }
    }
    throw new VerifyPropertyNameException();
}

corresponding method, refactored to LINQ

public void VerifyPropertyName(string propertyName)
{
    if (this.GetType().GetProperties().Any(propertyInfo => propertyInfo.Name.Equals(propertyName)))
    {
        return;
    }
    throw new VerifyPropertyNameException();
}

Test #4 (ViewModel implements INotifyPropertyChanged)
12) Create test for CustomerViewModel which verifies that CustomerViewModel is (of type) INotifyPropertyChanged.

[TestMethod]
public void Constructor_CustomerInput_ImplementsINotifyPropertyChanged()
{
    var customer = new Customer() { FirstName = "June", LastName = "Smith" };
    var customerViewModel = new CustomerViewModel(customer);
    Assert.IsInstanceOfType(customerViewModel, typeof(INotifyPropertyChanged));
}

Test #5 (ViewModel.PropertyChanged Event fires when ViewModel property setters called)
13) Create test for CustomerViewModel which verifies that after FirstName/LastName properties have been changed, that the PropertyChanged event has fired.

[TestMethod]
public void ClassProperties_WhenSet_PropertyChangedEventFires()
{
    var customer = new Customer();
    var sut = new CustomerViewModel(_customerRepository, customer);
    var receivedEvents = new List();
    sut.PropertyChanged += ((sender, e) => receivedEvents.Add(e.PropertyName));

    sut.FirstName = "Sabrina";
    Assert.AreEqual(1, receivedEvents.Count);
    Assert.AreEqual("FirstName", receivedEvents[0]);
    sut.LastName = "Moore";
    Assert.AreEqual(2, receivedEvents.Count);
    Assert.AreEqual("LastName", receivedEvents[1]);
}

event raising implementation

protected virtual void OnPropertyChanged(string propertyName)
{
    this.VerifyPropertyName(propertyName);

    var handler = this.PropertyChanged;
    if (handler == null) return;
    var e = new PropertyChangedEventArgs(propertyName);
    handler(this, e);
}

example of call to OnPropertyChanged within a property setter

public string FirstName
{
    get
    {
        return _customer.FirstName;
    }
    set
    {
        _customer.FirstName = value;
        OnPropertyChanged("FirstName");
    }
}

Test #6 (Domain entity properties stay in sync when ViewModel property setters called)
14) Create test for CustomerViewModel which verifies that after FirstName/LastName properties have been changed, that the Customer domain entity properties still match (i.e. have stayed in sync.).

[TestMethod]
public void DomainProperties_ReassignedValues_ViewModelPropertiesTrackChanges()
{
    var customer = new Customer() { FirstName = "June", LastName = "Smith" };
    var sut = new CustomerViewModel(_customerRepository, customer);

    Assert.AreEqual(customer.FirstName, sut.FirstName);
    Assert.AreEqual(customer.LastName, sut.LastName);

    customer.FirstName = "New First Name";
    customer.LastName = "New Last Name";

    Assert.AreEqual(customer.FirstName, sut.FirstName);
    Assert.AreEqual(customer.LastName, sut.LastName);
}

15) Refactoring: Move the interface, the PropertyChanged event, and all related methods into a superclass, ViewModelBase. Ensure that all tests are still passing.

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public void VerifyPropertyName(string propertyName)
    {
        foreach(var propertyInfo in this.GetType().GetProperties())
        {
            if(propertyName == propertyInfo.Name)
            {
                return;
            }
        }
        throw new VerifyPropertyNameException();
    }

    public virtual event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        this.VerifyPropertyName(propertyName);

        var handler = this.PropertyChanged;
        if (handler == null) return;
        var e = new PropertyChangedEventArgs(propertyName);
        handler(this, e);
    }
}

With all 6 tests passing, the Windows Phone emulator should display the tests as follows:

You are now ready to add event-handling to the ViewModel, by way of command classes.
Test #7 (CustomerSaveCommand implements ICommand)
16) Create CustomerSaveCommandTests.cs class
17) Add the following using statements:

using Microsoft.Silverlight.Testing.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;

18) You will need the same FakeCustomerRepository instance for your tests, so copy the instance variable and the test setup method from the CustomerViewModelTests class.

private ICustomerRepository _customerRepository;

[TestInitialize]
public void SetUp()
{
    _customerRepository = new FakeCustomerRepository();
}

19) Create test for CustomerSaveCommand which passes the _customerRepository instance, and a new Customer instance, as parameters to the constructor, then verifies that CustomerSaveCommand is (of type) ICommand.

[TestMethod]
public void Constructor_CustomerRepositoryInput_ImplementsICommand()
{
    var customer = new Customer() { FirstName = "June", LastName = "Smith" };
    var sut = new CustomerSaveCommand(_customerRepository, customer);
    Assert.IsInstanceOfType(sut, typeof(ICommand));
}

Test #8 (CustomerViewModel.CustomerSaveCommand.CanExecute returns true when Customer is valid, false when Customer is not)
You will now attach CustomerSaveCommand as a property of the CustomerViewModel class, so the remaining tests will be written against CustomerViewModel.
20) Return to the CustomerViewModelTests.cs class.
21) Create a test for the CustomerViewModel.CustomerSaveCommand property which verifies that sut.CustomerSaveCommand.CanExecute(object param) is true.

[TestMethod]
public void CustomerSaveCommandPropertyCanExecute_ValidCustomer_ReturnsTrue()
{
    var customer = new Customer() { FirstName = "June", LastName = "Smith" };
    var sut = new CustomerViewModel(_customerRepository, customer);

    Assert.IsTrue(sut.CustomerSaveCommand.CanExecute(null));
}

22) Refactor the CustomerSaveCommand class so that the condition under which CanExecute(object param) is true is that the properties of the _customer instance all have saveable values (they aren't null or empty.)

public bool CanExecute(object parameter)
{
    return !string.IsNullOrEmpty(_customer.FirstName)
        && !string.IsNullOrEmpty(_customer.LastName);
}

23) Now test the reverse, where passing a Customer instance with empty FirstName/LastName properties to CustomerSaveCommand.CanExecute(object parameter) returns false.

[TestMethod]
public void CustomerSaveCommandPropertyCanExecute_InvalidCustomer_ReturnsTrue()
{
    var customer = new Customer() { FirstName = "", LastName = "" };
    var sut = new CustomerViewModel(_customerRepository, customer);

    Assert.IsFalse(sut.CustomerSaveCommand.CanExecute(null));
}

Test #9 (CustomerViewModel.CustomerSaveCommand.Execute saves the Customer record)
20) Create test for CustomerViewModel.CustomerSaveCommand.Execute() method which verifies that the _customerRepository.SaveCustomer() method had been called.
NOTE Since we can't use a mocking framework such as RhinoMocks in .NET Compact Framework, we will add a test-only property to ICustomerRepository called SaveMethodWasCalled; set to false in the repository constructor, and set to true in the ICustomerRepository.SaveCustomer(Customer customer) method.

[TestMethod]
public void CustomerSaveCommandPropertyExecute_ValidCustomer_RepositoryValidatesCallOccured()
{
    var customer = new Customer() { FirstName = "June", LastName = "Smith" };
    var sut = new CustomerViewModel(_customerRepository, customer);

    sut.CustomerSaveCommand.Execute(null);
    Assert.IsTrue(_customerRepository.SaveCommandCalled);
}

CustomerSaveCommand.Execute() code

public void Execute(object parameter)
{
    if (this.CanExecute(null))
    {
        _customerRepository.SaveCustomer(_customer);
    }
}

FakeCustomerRepository.SaveCustomer() method code

public void SaveCustomer(Customer customer)
{
    // call to not-yet-created WCF client web method
    _saveCommandCalled = true;
}

22) In Solution Explorer, right-click on the solution and choose "Deploy Solution". All 10 tests should be passing. Click on either test class in the Win Phone 7 emulator window to view the complete list of tests.
With all tests passing, you are ready to proceed to the final step - hooking up the ViewModel to the XAML presentation layer.

[Postscript - Building the XAML presentation layer mapped to ViewModel]
23) In Solution Explorer, double-click MainPage.xaml to open it. Adjust the split view between the design and XAML views so that you can easily edit the XAML.
24) In the <phoneNavigation> element, after the xmlns:x attribute, type xmlns:vm= and Intellisense will appear with a list of optional namespaces containing the viewmodel. Press the down arrow to select the ViewModel namespace (as shown below), and then press Enter.
25) Your viewmodel namespace reference should now look like this:

25) Next, you must associate the DataContext of MainPage.xaml to the specific view model class for this page, which is CustomerViewModel.cs. This can be achieved declaratively in the <UserControl.Resources> element of the XAML, but only when your view model has a parameterless constructor. For CustomerViewModel, we will need to associate the DataContext in the code-behind class. To do this, go to the first test you created in CustomerViewModelTests, and copy the instantiation code. Paste it at the end of the MainPage constructor. You'll need to use Alt-Shft-F10 to resolve various references to your classes, and add an instantiation to FakeCustomerRepository (This could be switched out for a "real" repository class later, eg. one built as a wrapper for a web service proxy addressing your web service layer.). When you are done, the data context assignment code should look like this:

ICustomerRepository customerRepository = new FakeCustomerRepository();
var customer = new Customer();
var customerViewModel = new CustomerViewModel(customerRepository, customer);
this.DataContext = customerViewModel;

26) Drag 2 TextBox controls with labels (i.e. TextBlock controls) and a Button onto the design surface. Apply names and pretty-fication until it looks something like this:

27) Add the following attribute to the corresponding XML element for each TextBox (where Path equals the property name from the ViewModel)
for txtFirstName control

Text="{Binding Path=FirstName, Mode=TwoWay}"

for txtLastName control

Text="{Binding Path=LastName, Mode=TwoWay}"

28) You are now ready to add binding to the "SaveCustomer" button.
NOTE At this point, in WPF, the Button's Command property could be set to the data binding for the SaveCustomerCommand class. However, the Command property doesn't exist in Silverlight. This issue has been addressed in a blog entry by Patrick Cauldwell. In the steps below, his ButtonService class is used to provide support for a custom Command binding attribute.

29) Download Patrick Cauldwell's code zip (located at bottom of his blog entry.)
30) Extract the code zip, and locate the ButtonService.cs class.
31) Add a new folder/namespace to your existing folders, named "ThirdParty". Drag and drop ButtonService.cs into this folder.
32) Open the ButtonService.cs class, and change the namespace to match the path of your folder.
NOTE 2010Mar27 As of this writing, there is a TYPO in the ButtonService.cs class. In the initial static registration of the CommandParameterProperty, the 2nd parameter (see code snippet below) is set to typeof(string). This will cause a null error later, in the event raising method, when attempting to cast the command instance to ICommand (because it incorrectly being passed as string.) Therefore, CHANGE the 2nd parameter to be typeof(ICommand).

public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.RegisterAttached("CommandParameter", typeof(string), typeof(ButtonService),
        new PropertyMetadata(OnCommandParameterChanged));

33) In MainPage.xaml, you need to add an alias to this namespace (similar to the xmlns:vm namespace you created earlier.) eg. "thirdParty". Add this directly below the xmlns:vm namespace reference.

xmlns:thirdParty="clr-namespace:CustomerApp.Support.ThirdParty"

34) Add a new attribute to the <Button> element to provide data binding to the CustomerSaveCommand.
NOTE 2010Mar27 Path must be bound to the ViewModel's property name, NOT the property type (if they are different; for example, if the property were CustomerViewModel.Save of property type CustomerSaveCommand, then the correct binding path would be "Save").

thirdParty:ButtonService.Command="{Binding Path=CustomerSaveCommand}"

35) You now need to turn off your tests to run your application. BEFORE you do that, run your tests one more time and make sure that all tests are passing.
36) With all tests passing, let's run the application now instead of the tests. To do this, go back to the MainPage.xaml.cs and switch the boolean constant (runUnitTests) to false:

const bool runUnitTests = false;

37) In the FakeCustomerRepository.cs, set a breakpoint on the line of code within the SaveCustomer() method.
38) Finally, launch the application. Enter values into the FirstName and LastName fields, and press the SaveButton.
49) Observe that the breakpoint is hit, the Customer is being passed correctly to the Customer repository's Save command, and the value of the Customer FirstName and LastName properties match the values you entered into the TextBox controls in the XAML layer.

Your Win Phone 7 application using MVVM and unit tests is now complete.

63 comments:

Keith said...

Great Tutorial/Walk-Through!

Looks to be a type-o/issue in step 12.

var customerViewModel = new CustomerViewModel(customer);

should be...

var customerViewModel = new CustomerViewModel(_customerRepository, customer);

Nigel said...

I get a VerifyPropertyNameException was unhandled. Using the latest VS Phone build. Am I missing something?

mickey said...

Updated Silverlight Unit Test Framework bits for Windows Phone http://www.jeff.wilcox.name/2010/05/sl3-utf-bits/

WWallace said...

I'm new to this. Test 1 did not compile for me until I added a Customer class (with 2 properties) and a CustomerViewModel class (with suitable constructor). I put them in the Domain and ViewModel folders, respectively.

Is this correct?

kimberly said...

This whole information is absolutely useful and interesting. i like this blog because is easily understandable, and that is invaluable to the readers. I wanted to buy viagra online, and i saw this blog. I think this information will be useful for me specially because i am a person who like to read every kind of theme. Thank you for share with us this information.

brinkka said...

Erectile Dysfunction FAQ's

Impotence, or penile erectile dysfunction, in men is not as rare as one would think it is.
In fact, one out of ten men around the world is affected by this condition. It is not exclusive to old men either. Impotence is caused not just by physical factors,
but mental factors as well.
Exercising on a regular basis and eating healthy food will increase your ability to sustain prolonged physical effort and we all know that sex is effort.
Smoking, frequently drinking large quantities of alcohol, taking recreational drugs and lack of exercising will sap your stamina and also slow down the flow of blood.
Penis exercises can be used for maintaining fitness, preventing or controlling premature ejaculation and increasing the length and girth of the penis.
by penis enlargement pills you can overcome these problems and not only that you can also enlarge, extend also to strengthen your penis and premature ejaculation will not exist anymore for you.
visit http://www.factspenisenlargement.com for more info.

Topo said...

What a great idea!! i'ld like to know more how many people you met on 1st sept, i'm quite late to see this blog.
Mr. president doing great with health issues, vigrx plus just few day's before FDA release new rules of packed food products to display all the details of energy and this are issued with taking public opinions from allover country.

Very good! Nice information this is really interesting. Good luck in building the table.

vigrx plus | vimax pills | penis enlargement pills | penis enlargement

male said...

For men who want bigger, harder, longer-lasting erections, there's now VigRX Plus™, a fresh twist on the already popular VigRX™, but designed to further enhance men's sexual functioning with the addition of three exciting new ingredients: Damiana, Tribulus, and Bioperin. Doctor endorsed and rated #1 for results by clients of penis enlargement consumers. rated two penis pills is vimax. if you find about male enhancement this products is the best and proven to work, there products have money-back guarantee in effectiveness and result.

kiara said...

Costa Rica Tours
Tours Costa Rica

Christian said...

I've got an unofficial port of NUnit for Silverlight that plays nicely on Phone 7.

http://code.google.com/p/nunit-silverlight/

marzia said...

nice post....and great blog!!
Levitra Generico

Crist.. said...

Really you have done great job,There are may person searching about that now they will find enough resources by your post.I like this blog..
Blogger how when you visit my site,
My Site created for men's health,
Here the best method for you and get the best Penis Enlargement Pills today.

kimnellen2587 said...

These kind of post are always inspiring and I prefer to read quality content so I happy to find many good point here in the post
temporary tattoo

penisenlargement4male said...

It is best to participate in a contest for among the best blogs on the web. I'll recommend this website!
Vimax Pills Enhance VigRX Plus metropathies Male Extra Amp Do VigRX Plus pills really work mastoidal

kimnellen2587 said...

I certainly enjoyed the way you explore your experience and knowledge of the subject! Keep up on it. Thanks for sharing the infoAfrican Mango

kimnellen2587 said...

Happy to see your blog as it is just what I’ve looking for and excited to read all the posts. I am looking forward to another great article from you. African Mango

kimnellen2587 said...

These kind of post are always inspiring and I prefer to read quality content so I happy to find many good point here in the postbobble heads

kimnellen2587 said...

Thanks for the posting. Loads of excellent writing here. I wish I had found this site sooner
rotherham hypnotherapist

kimnellen2587 said...

very informative post, thanks for sharing it with usManchester photography studio hire

kimnellen2587 said...

I’ve seen progression in every post. Your newer posts are simply wonderful compared to your posts in the past. Keep up the good work
tattoo sales

Josh said...

Vimax is reliable stuff to accomplish your sexual dysfunction. It has been proven by many men to augment virility and genital.

jelish said...

The Louboutin 2011 is planned using the ladies. offered the fact that pumps was born, the ladies lifestyle turn into colorful. The christian Louboutin 2011 Pumps
will be the god's masterwork. Who invited the Christian Louboutin Wedges ? Seldom people knew, but I think each and every and every lady will be grateful for him. between the countless pumps,the Christian Louboutin Shoes
could be probably the most exceptional ones. The stylish pattern, the delicate design all mold the ladies perfect leg profile. Flowers inside the spring of 2011 creeping, up from frizzy hair to outfits hold on to footwear, have experienced a brilliant up. on this type of the glamor, spring and summer time flowers now here. Romance is really a woman's mood, exquisite flowers just appropriate of expression within our gestures, the woman, how can we not adore the romantic temperament to make certain which they distributed the flowers do? 2011 flowers bloom will get satisfaction from numerous poses! The Christian Louboutin New Sandalsalso can adds the hright in the ladies, it hold shock toward short lady. especially the red-colored lone in the louboutin heels, beauty and sexy, different ladies are crazy. The red-colored sole, the earliest attribute in the Sale Louboutin stroe.

truddy said...

Want to be the fashionable person?Sexy and beautiful women are always men's favor. Now come to Louboutin UK store! We will change you into a gorgeous girl! Here provides Christian Louboutin Fashion,Louboutin 2011,Christian Louboutin Wedding,louboutin platforms,Louboutin Wedges and so on.The unique and well-Designed shoes favored by the world, signed with the Christian Louboutin red soles of the mark, giving high heels and covetable accessories, system pumps and elegant luxury Christian Louboutin clutch has to be available almost all fashion only. We now provide Christian Louboutin shoes with free shipping and save 60% off! Hurry up! Christian Louboutin UK store is your first choice!

abercrombiefitch said...

Most women would love to own an authentic cheap christian louboutin shoes The problem comes when people try to match that goal up with finding a Christian Louboutin Outletthat fits their budget. We have heard stories that it is possible to find discounted designer shoes, but it is easy to get frustrated when the cheapest shoes you are able to find are priced well into the hundreds of dollars. Christian Louboutin DiscountLet's cut right to the chase. However desirable all but the wealthiest women may find designer label shoes, Louboutin Outletmost of us are going to have at best one or two cheap christian louboutin clearance shoes if we insist on buying new at retail.Christian Louboutin 2011 Christian Louboutin Ankle Shoes if we want to have an assortment of such treasures, we're going to have to make our purchases of lower-priced, Christian Louboutin Heelsdare I say cheap shoes, while not sacrificing original designer heritage for them.Christian Louboutin Peep ToeOf course, when shopping for cheap cheap louis vuitton bags one can put oneself at the mercy of charlatans.Sad to say, there are those among us who have no qualms about passing off a counterfeit designer bag as the real deal, and perhaps having a good laugh at our expense.Recognizing that,Christian Louboutin Pumpsunless one has grown up in a family of purse collectors or had some kind of similar experience of being around genuine articles of quality, Christian Louboutin Sandalssome education is in order. Some people suggest going to christian louboutin outlet stores and examining the goods found there.Christian Louboutin Tall ShoesSuch a plan can be an advantage, Ghd Australia in the outlet store.
Five Fingers Shoes
Happy to see this article as it is just what I have looking for and I am looking forward to another great article from you. You may be interested in CHI us
Your space is really pretty, have no interest in to my chi for sale space to share it? thank you Oh.Buy Nike Shoes

uggbootsoutlet said...

Our Christian Louboutin Outlet online store offers a variety of styles of shoes. Our website offers a variety of styles of shoes, these Christian Louboutin Online shoes are the favorite of girls. They are the best choice for you most to fashion. These Christian Louboutin Outlet Online shoes are one of our shops are selling shoes.

Alex Ray said...
This comment has been removed by the author.
Alex Ray said...
This comment has been removed by the author.
emma said...

Hi guys, I want to say thanks for this useful information, I shared it on my wall to be honnest cause I LIKE it(:Acheter cialis

LauraStarBene said...

Nice post! Thanks for sharing!
Viagra Generico

date: said...

my goal is that these steps could be repeatedly practised, like a kata, to develop a consistent approach to TDD using the Windows Phone 7 unit testing framework.turtle ridge homes

baele said...

This Nike mercurial vapor ultra vibrant Nike Mercurial Vapor Superfly III FG...The Superfly series has a huge army of fans across the globe, and if you're one of them we know you love them to be bright, flashy and in-your-face colours. Checking out these Nike mercurial vapor fg versions, part of Nike's Fall seasonal releases, we're sure you're going to love these Nike mercurial vapor too, especially once they start appearing en masse on pitch. Nike vapor soccer shoes You can click "VIEW MORE" for more detailed pictures. New levels of performance after Nike soccer cleats new levels of performance after,Nike Mercurial Superfly.

Chris Suja said...

Please continue to write more because it’s unusual that someone has something interesting to say about this. Will be waiting for more. Online Pharmacys

LauraStarBene said...

Nice post! Thanks for sharing!
Buy Levitra

weerah said...

This is really interesting, You're a very skilled blogger. I've joined your feed and look forward to seeking more of your wonderful post.
buy codeine online

Ваня said...

Благодарю за информацию действительно увлекательно, однако меня также интересуют и новости голографии. Надо же развиваться в области голографии

mark said...

buy online percocet
Thank you, I have recently been searching for information about this topic for ages and yours is the best I have discovered so far.

weerah said...

This is really interesting, You're a very skilled blogger. I've joined your feed and look forward to seeking more of your wonderful post.
buy xanax online

Mark said...

Thank you for the info great! I try to watch what I regard as my body tries to digest, so I learn that I am bamboozled any damn time!


buy watson online

buy vicodin es online

buy ritalin online
buy xanax online

Ricky Martin said...

Very significant article for us ,I think the representation of this article is actually superb one. This is my first visit to your site.


buy online vicodin es

Muhammad Alexander said...
This comment has been removed by the author.
Ana Boks said...

In this post you have shared so many useful information. I was pleased to read this post i have never sen this before.

Facebook Developer New York

mark said...

cost effective advertisingVery significant article for us ,I think the representation of this article is actually superb one. This is my first visit to your site

suzan said...

Thank you very much for this usefull information! I really understand the topic now!


maquinas de coser
maquinas de bordar

mark said...

Thank you, I have recently been searching for information about this topic for ages and yours is the best I have discovered so far.drug discussion
drug discussion forum
drug forum

Silviu said...

It the first time that I try to make a Windows Phone 7 app and I will use your advice.

Liviu metode de facut bani Dessey

Dr Qual said...

the c# devolovment emt salary is a realy good language.

suzan said...

Thanks for your post, I like this post very much.
doctor ratings and reviews
reviews by patients
find doctor list

saim said...

Thanks for such an interesting article here. I was searching for something like that for quite a long time and at last I have found it here.

find doctor list

Colin said...

Many thanks for the article. I will have a link back to this information from our fresh blog. Thanks again.
buy watson

Kyle Grando said...

Very interesting blog post here. Your article provides a fresh new insight to this topic which was yet undiscovered. I must say your research skills are sharp and your narration is interesting. Thanks for posting it.

CHEAP HOTELS DENVER CO

ocs123 said...

My name is Louis Vuitton handbags,your post is very good! Do you like gucci bags any more!

Amalia Adrian said...

It's quite interesting, I've ever quite understand this matter but now thanks to you I do. I can't wait to read more articles from you.

Capstar

Hubert Haley said...

Quickly sprinkle Acai Berry on the cheese so that it can melt and become gooey. (If it does not melt enough to your liking microwave it or place it Quick Weight Loss Tips under the broiler for a few minutes.)
Toss the avocado in lime juice and sprinkle Online Pharmacy Australia onto the dip, followed by the tomato, jalapenos, sour cream, olives and green onions.

ceoworld.biz said...

Accounts receivable factoring (or invoice financing) has been around for centuries as a means for small and large businesses to obtain needed working capital while they wait for their customers to pay invoices.Read more here: http://obsbankwatch.blogspot.com/2012/01/bank-of-commerce-posts-847000-loss.html#storylink=cpy
http://ceoworld.biz/ceo/2012/04/24/startups-invoice-factoring-your-ability-to-grow-your-access-to-cash

cheap sale online said...

Thanks for sharing, like your post very much. Welcome to buy flirt dresses p4530, we also sale la femme 16898 and night moves dress 6498, most free shipping, high quality guarantee!

MALE ENHANCEMENT said...

I just added this post as a favorite.
because it is very interesting and provide insight to readers ..
thanks

Penis Enlargement Pills
Vimax Pills
Vimax Extender

Lulu Hostan said...

Your Post Is Good, Go On With It. And Welcome To Sac Longchamp Outlet To Have A Look And Find Your Favourite Longchamp Paris Here!

cheap sale online said...

Impressive! You have got the key point. Autumn coming, and winter coming, those are ready for the cold season should come to UGG boots outlet online store to pick up the UGG boots cheap.

Lulu Hostan said...

Your Post Is Good, Go On With It. And Welcome To Sac Longchamp Outlet To Have A Look And Find Your Favourite Longchamp Paris Here!

Lulu Hostan said...

Your Post Is Good, Go On With It. And Welcome To Sac Longchamp Outlet To Have A Look And Find Your Favourite Longchamp Paris Here!

Jasminder said...

good
do you know how should i start building a new app to launch it in market place.

mahasiswa teladan said...

hi...Im student from Informatics engineering nice article,
thanks for sharing :)

Dinah Bee Menil said...

Stop wasting your money from paying unnecessary bills. Use free international mobile calls app to help you save money. Know more details by visiting their website at http://www.chitrchatr.com/en/