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
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.
[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.
33 comments:
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);
I get a VerifyPropertyNameException was unhandled. Using the latest VS Phone build. Am I missing something?
Updated Silverlight Unit Test Framework bits for Windows Phone http://www.jeff.wilcox.name/2010/05/sl3-utf-bits/
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?
I've got an unofficial port of NUnit for Silverlight that plays nicely on Phone 7.
http://code.google.com/p/nunit-silverlight/
nice post....and great blog!!
Levitra Generico
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
Thanks for the posting. Loads of excellent writing here. I wish I had found this site sooner
rotherham hypnotherapist
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
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
Nice post! Thanks for sharing!
Viagra Generico
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
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.
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
Nice post! Thanks for sharing!
Buy Levitra
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.
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
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
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
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
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
the c# devolovment emt salary is a realy good language.
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
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
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.
good
do you know how should i start building a new app to launch it in market place.
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/
NUTRITIONAL FACTS AND WONDERFUL HEALTH BENEFITS OF CASHEW NUTS
http://depositcepat.com/
Poker Deposit Pulsa
Hey I’m Martin Reed,if you are ready to get a loan contact.Mr Benjamin via email: 247officedept@gmail.com ,WhatsApp:+1 989-394-3740 I’m giving credit to his Service .They grant me the sum 2,000,000.00 Euro. within 5 working days.Mr Benjamin work with group investors into pure loan and debt financing at the low ROI to pay off your bills or buy a home Or Increase your Business. please I advise everyone out there who are in need of loan and can be reliable, trusted and capable of repaying back at the due time of funds.
Post a Comment