Let’s take a chilled out walk through adding Xamarin.UITests to your Xamarin.Forms project! 😉
When I said chilled out, I meant literally a chill, no-fuss walk through adding UITests for your Xamarin.Forms project solution. There’s many articles and tutorials out there regarding this, but when I first started I couldn’t find a clear enough guide to begin with for myself, so I was stumbling around in confusion here and there until I self-learned a proper foundation. That’s why I thought of writing this post.
This is an attempt of sharing my experience and intuition of how to architect a better UITest structure for your Xamarin.Forms Solution helping you to get a clear and easy head start!
So I’m not gonna get into extreme baby steps, or details, but a clear fuss-free hands-on guide for starting off with your UITests, which I hope would give you a clear picture and understand of the whole shabang! After all I’m all about that solid project architecture!
Why UITests?
Hey, if you’re looking for a serious intro, please Google! I don’t like repetition of content lol.
Xamarin.UITests comes in handy when you want to have consistent assurance of the actual functionality of the app with the UI behaviour included. And between you and me, I actually love watching UITests being executed on Devices and Simulators, seeing you app actually being used like a human, giving you a whole feedback loop of the UI behaviour , is just an incredible experience! 😉
Let’s get started..
Just for the showcase of this awesomeness, I created a little App, which is called Textpad, where you simple take notes or texts of whatever you feel like. 😉 A very simple out of the box Xamarin.Forms app, and fully MVVM architectured code base with Prism. I named the solution as “XFWithUITest” just for this demo.
Whatever the default template of the Xamarin.UITest has provided, I have done several changes to it here and there for the clarity and of the code base as you will see in this article.
So I’m gonna walk you through a clean and well-structured manner of adding Xamarin.UITests to your project solution.
You can take a little sneak peak at it over here in my github repo:
XAMVVM-Playground/XFWithUITest
Structure is important!
There’s many ways to structure a UITest, but I like a clean separation of the elements in any solution architecture. Like here we’re going to separate our Tests from the actual Host projects.
So first, for the name of separation let’s add a Folder called “Tests” in your Xamarin.Forms solution. Yes, that’s the way to start!
Then let’s create our Xamarin.UITest project, right-click on the “Tests” folder in the VS Solution Explorer and go to Test tab and select Xamarin.UITest Cross-Platform Test Project!
Also pay extra attention to the Name and Location value for our UITest project. Append “.UITest” at the end of your project name. As of the location, make sure to add the path along with the “Tests” folder that we created above.
Next create a new Folder inside of that project called “Tests”, yes another one, which is where we’re actually placing our tests! Also create a new class called SetupHooks, which is where we’ll maintain all the hooks that are needed for our tests. (I’ll get into details for this in a later step)
Now it should look something like this!
Nothing more.
Delete anything else that’s unnecessary or not seen above! 😉
Off to next step!
Don’t forget the nugets!
Make sure all the necessary nuget packages are in place, which is just basically the following 3 nugets! yep that’s it!
Pay very careful attention here to the version of NUnit version 2.6.4, which is the minimum NUnit version supported by Xamarin.UITest as of today. (01/11/2018)
The deal with AppInitializer!
Now this right here is where your Tests will be firing up the app’s execution. There are many ways to structure this class and its functionality, but here’s my way…
This class comes pre-populated when you first create the UITest project, but I have made some changes of my own for the clarity of the code.
As you can see I’m passing in an extra boolean parameter “clearData”, which is to execute a clean instance of my App for testing.
I’m using the InstalledApp() call to load the Android and the iOS apps from the simulators, also I’m enabling the EnableLocalScreenshots() to get actual screenshots of my test instances as I wish. Yeah the fact that you can automatically capture screenshots during testing even when you run locally is really cool feature of Xamarin.UITests! 😉
Now instead of getting a hook on the InstalledApp(), you could use the path to the APK or IPA file using the ApkPath() or AppBundle() respective for Android and iOS, which is totally up to your choice.
Then I’m passing in the AppDataMode parameter according to my choosing of the “clearData” value.
SetupHooks holds the instances!
Remember earlier I created a class called SetupHooks? let’s set it up now!
public class SetupHooks { public static IApp App { get; set; } public static Platform Platform { get; set; } }
During UITests execution we’re holding a singular instance of the app in memory, which we’re calling through UITest’s functions to perform many operations, so to simplify that, here we’re holding a public static instance of the IApp and Platform object to be used in our Test cases.
Pretty neat eh! 😀
Let’s write the Tests!
Create a class called AppTests, which is where we’re going to place the Test fire up code and the rest of the tests for now!
namespace XFWithUITest.UITest.Tests { [TestFixture(Platform.Android)] //[TestFixture(Platform.iOS)] public class AppTests { public AppTests(Platform platform) { SetupHooks.Platform = platform; } [SetUp] public void BeforeEachTest() { SetupHooks.App = AppInitializer.StartApp(SetupHooks.Platform, true); } [Test] ... // test cases begin here... } }
There I have added the TestFixture attributes as required by NUnit to identify our tests, and notice how I have commented out the iOS platform, to show you that you could stick to one platform at a time for your ease of testing, instead of seeing failed tests in the Test Runner window! 😉
[SetUp] is where your Tests will initialize the actual App instance, thus retrieving a hook to the app’s instance for our Test cases to use.
You can see how I’m tying up the SetupHooks – Platform and App instances, through the initiation of the AppTests.
AppInitializer.StartApp(SetupHooks.Platform, true);
This gives a clean instance of the app for our tests cases to use, and up on your wish you could pass in “false” to the same method and get a data persisted instance of the app at anytime, anywhere in your tests! 😉
Now you’re all set to start writing your UITests, but before we begin I need you to check up on something else!
AutomationId for everything!
Whatever the UI element you need to get a hook on to or get a reference of, be it a Page, Button, Layout even a Label, you need to add a value to its AutomationId.
And make sure every AutomationId in a given Page context is unique for every element, otherwise the look up function will return all the elements that matches the given Id, which could lead to confusion in your tests 😉
IApp interface functions!
The Xamarin.UITest.IApp interface provides a whole bunch of functionalities for the app for us to play around with in order to execute our test scenarios.
Take a look here, Xamarin.UITest.IApp to see the list of powerful functions we can use. To name a few are Tap, Swipe, Scroll, WaitForElement and etc, to be performed on any given UI Element on the screen.
So now all you need to do is get a hook on any given element..
Getting a hook on an Element…
There’s several ways of doing this, most common is by the AutomationId of the Element
SetupHooks.App.Tap(c => c.Marked("Button1"))
Another is by the value of an Element’s property,
SetupHooks.App.Tap(c => c.Text("Click this Button!"))
Or you could do by even the Class name of the element. Choice is completely yours, pick the one best suited for your test case.
How to write a Test?
Now this is the coolest part, Xamarin.UITest allows us to get hooks on to UI Elements of the running App, then we perform actions on those elements and wait for the results and check if it resulted as expected through assertion using NUnit.
So its basically a little dance between Xamarin.UITest and NUnit Assertion! 😉
As a standard keep in mind to append “Test” at the end of each of your Test cases.
As you can see above I’m first waiting for the HomePage to appear, then I’m asserting it through NUnit. Then I look for the Label with “Hey there, Welcome!” text!
Action and Result, simple as that! 😀
Some advanced bits…
Here’s some advanced bits that could come in handy!
Getting the number of elements in a ListView
SetupHooks.App.Query(c => c.Marked("TextListView").Child()).Length
Getting an element in a ListView
Func<AppQuery, AppQuery> itemInListView = null; if (SetupHooks.Platform == Platform.Android) itemInListView = x => x.Class("ViewCellRenderer_ViewCellContainer").Index(0); else if (SetupHooks.Platform == Platform.iOS) itemInListView = x => x.Marked("<your listview automationId>").Index(0); // change the index parameter to get the item you wish
Opening Context Menu in a ListView item
// pop up the Context menu in ListView item if (SetupHooks.Platform == Platform.Android) SetupHooks.App.TouchAndHold(firstCellInListView); else if (SetupHooks.Platform == Platform.iOS) SetupHooks.App.SwipeRightToLeft(firstCellInListView);
Enter Text into an Entry or Editor
SetupHooks.App.EnterText( c => c.Marked("TextTitleEditor"), whateverYourText);
Wait for an element to disappear
SetupHooks.App.WaitForNoElement(c => c.Text("This label text")); // either by Text or Marked as should work
Restarting the app anywhere…
// restarting app, persisting state SetupHooks.App = AppInitializer.StartApp(SetupHooks.Platform, false);
Check out more here in this awesome git page: XamarinTestCloudReference
REPL is your tool!
Yes start using the REPL command line to see how your App’s UI is actually rendered by the native platform at any given execution time. Simply call this anywhere you wish in the UITests steps,
App.REPL();
And you’ll be presented with a CLI which will help you see the whole UI tree of the screen. Simply type “tree” in the CLI and you’re good!
Structuring the tests..
Now there’s many ways to structure all the test cases and scenarios, and there’s no strict standard way that should be followed, but whatever you’re comfortable or fits your project is totally fine and the choice is yours!
You could include all your Test cases in the AppTest class itself, or you can break them into separate classes regarding the Page, or the functionality type.
So for this demo I’m keeping all my UITest cases in the AppTest class itself.
Running the UITests locally!
Well now that we have structured the architecture, here’s the time for actual firing things up and you’ve got couple of things to remember!
You can run your Android Tests on Simulator and Device directly without any modification as long as you provide the right APK path or the App Id.
You can run your iOS Tests only on Visual Studio for Mac, and for the device you need to pass the provisioning details, and as of simulator, you need to pass the Simulator Id.
If you’re using InstalledApp() or ConnectToApp() in your AppInitializer, then make sure the app is already deployed or running in the devices or simulator.
Also make sure to keep your Devices or Simulators or Emulators screens switched on at all times, otherwise tests will break giving a waiting exception.
That’s it!
But I’m not completely satisfied with the architecture, so let’s kick it up a notch! 😀
Little cherry on top Architecture!
Like I said before there’s many ways to construct the architecture for your Test project, one of my favourite ways is by separating the test cases by Page Scenario, which I think is a much cleaner structure.
We’re going to create a base class, “TestBase” which has the constructor initiation and BeforeEachTest setup, then create a sub classes that inherits from it representing whatever the pages we have in the App.
It should look something like this!
And don’t forget you need to add TestFixture attribute for every single sub-class!
So what you’re gonna do is take apart all the Test cases you had in one class and move them into the related pages, simply cut and paste of the methods should do! Also on top of that you could abstract another layer of shared steps that we could reuse across these Page tests. 😀
Then it should give you a clean Test output as below.
There you go, all the Tests are now nicely aligned and structured under the given Page which it associates with!
Pretty neat eh!
So this above structure of mine is somewhat more of a simplification of the Page Object Architecture which is well explained here for Xamarin.UITests: https://www.codetraveler.io/
And even in this official github sample from Xamarin uses the same similar pattern: SmartHotel.Clients.UITests
Done!
As you can see its not that hard to set up your Xamarin.Forms project with UITest once you get the basic understanding of the moving parts and keep a clear structure in your head.
Now for some of you might be experiencing some issues with Xamarin.UITest, in which case I had too when I was first starting off. Therefore I ended up writing this post sharing my experience of solving them: Getting your Xamarin UITests to actually work! So if you’re having any issues getting your Xamarin.UITests to work in Visual Studio, that post might be able to help you. 🙂
Do check out my Github repo of this post:
XAMVVM-Playground/XFWithUITest
Thus concludes my real walk-through of Xamarin.UITests with Xamarin.Forms, in which I hope you got a clear understanding of how to properly structure your project and all the moving bits and pieces that gets the job done! 😀
Share the love! 😀
Cheers!