Skip to content
stackunderflows edited this page May 21, 2017 · 5 revisions

There are four main rules that we follow to keep our test suites consistent and useful. We learned from trial and error and found that these were the most important concepts to be aware of in order to be successful.

1. Each page in the app is a class in your test code

There should be a one to one mapping between pages in the app and page representations in your test code.

If you navigate away from a page or if a modal view covers the page so that you can no longer interact with the page underneath, then it should be considered a new page and you should define a new page class in your test code.

The following diagram illustrates what this should look like. Your test method calls into various pages that communicate with the corresponding pages in your app via Xamarin.UITest. Notice that each page in the test suite maps to a single page in the app.

Page object pattern diagram

2. Page representations know about all the elements on that page

Page classes save queries for all the elements on the page as fields at the top of the file.

Don't define any queries inside methods. Define them all as fields at the top of the class and assign them in the constructor based on the platform. This makes it easy to reuse and change the queries as needed.

public class LogInPage : BasePage
{
	readonly Func<AppQuery, AppQuery> EmailField;
	readonly Func<AppQuery, AppQuery> PasswordField;
	readonly Func<AppQuery, AppQuery> LogInButton;

	protected override PlatformQuery Trait => new PlatformQuery
    {
        Android = x => x.Id("log-in-image"),
        iOS = x => x.Id("sign-in-image")
    };

	public LogInPage()
	{
		// The same for both platforms
		LogInButton = x => x.Id("log-in-button");

		if (OnAndroid)
		{
			EmailField = x => x.Id("android-email-field");
			PasswordField = x => x.Id("android-password-field");
		}

		if (OniOS)
		{
			EmailField = x=>x.Id("iOS-email-field");
			PasswordField = x => x.Id("iOS-password-field");
		}
	}
}

Also, each page must define a Trait property

The "trait" of a page is a query for some element unique to that page. The BasePage uses a WaitForElement to ensure that the trait appears (i.e. the page is fully loaded) before you can execute any methods on the page.

3. Page classes define methods for all the actions that can be taken on that page

You should only interact with a page in the app through methods defined by the page representation.

If a method navigates away from the page (e.g. taps a button that goes to another page in the app), the method should return void since you can no longer call more methods on that page.

Methods that do not navigate away from the page should return their own type (using return this;) so that more methods can be called on the same object. This creates a fluent interface that is easy to work with.

new LogInPage()
	.EnterCredentials("name", "password")
	.ConfirmLogIn();

The benefit of the fluent interface is that you don't need to save the page to a variable so the code can be more concise.

4. Test methods consist only of calls to page methods

There should be no calls to Xamarin.UITest in test methods, only in page methods.

Take another look at the above diagram and notice that only the the page representations ever interact with pages in the app via Xamarin.UITest. The test method never calls into UITest directly.

Don't use app. in [Test] methods. That is the pages' job. Tests should only instantiate new pages and make calls to those pages like so:

[Test]
public void LogInAndExploreTest()
{
	new LogInPage()
		.EnterCredentials("name", "password")
		.ConfirmLogIn();

	new HomePage()
		.SelectFirstItem();

	// ...
}
Clone this wiki locally