Having used the likes of TestStack.White in the past I was interested in trying out the Xamarin UITest library for executing tests against my mobile application’s UI.
For now I’m primarily discussing testing against an Android device or emulator, but I will amend the post as and when I run tests against iOS.
Some problems and solutions
It didn’t go smoothly, initially, getting a project running. Hence, this part of the post is going to cover some of the issues I had and some of the possible solutions.
- If you have platform-tools.old followed by a number of digits within C:\Program Files (x86)\Android\android-sdk (for example platform-tools.old1970091580), either delete these folders or for safety’s sake, move them outside of the C:\Program Files (x86)\Android\android-sdk folder, otherwise running the UITests seems to get confused which adk it should use.
- Add an environment variable (if not already existing) named ANDROID_HOME and set it’s value to your install of the android-sdk, i.e. C:\Program Files (x86)\Android\android-sdk
- The version of the UITest NuGet package seems to be related to the version of the Android SDK, so after you’ve created a UITest project and added the Xamarin.UITest package, depending on the Android SDK you might need to get a previous version of this package – in my case I’m using Xamarin.UITest version 2.2.7.0 and Android SDK Tools 26.1.1
- For an Android application, we need to set the project properties Android Manifest, Required permissions to include the INTERNET permission
- For Android projects we need to run against a Release build
- Again for Android projects, if we added a UITest project which include the ApplInitializer.cs file, we need to supply the application name or the path to the .apk file being used, for example
if(platform == Platform.Android) { return ConfigureApp .Android .InstalledApp("com.putridparrot.MyApp") .PreferIdeSettings() .EnableLocalScreenshots() .StartApp(); }
Note: In place of InstalledApp we could use ApkFile with the path of our apk file, i.e.
.ApkFile(@"C:\Development\MyApp\com.putridparrot.MyApp.apk")
- If you’ve previously installed your application on the emulator or device, I found it best to manually uninstall it first
- Before running your tests, make sure the emulator is running or the device is connected (I know, probably obvious)
Creating our UI test project
- Add a Xamarin.UITest Cross-Platform Test Project to your solution
- Build the project so it restores any missing NuGet packages, I needed to update the following packages to Xamarin.UITest version 2.2.7.0 and NUnit 3.11.0.0
- Deploy your application to your device before running the tests
- When running your tests, they may take a little time before you see anything happening on the emulator or device – so don’t be too impatient.
Before we can actually run our tests (which we’ll discuss next) we need to build our application and generate an APK for Android (if running against Android).
Writing our UI tests
To allow our UITests to locate controls in our user interface we supply each control (that we need to interact with) with an AutomationId, this should be unique (so it’s easy for our tests to locate).
For Android application I found this configuration works (obviously replace the ApkFile location/name with your apk).
if(platform == Platform.Android) { return ConfigureApp .Android .ApkFile(@"C:\Development\MyApp\com.putridparrot.MyApp.apk") .PreferIdeSettings() .EnableLocalScreenshots() .StartApp(); }
Note: we should be able to replace the ApkFile line with .InstalledApp(“com.putridparrot.MyApp”) if you prefer.
The UITest project will require an AppInitializer.cs file which, if you created via the project template, will be included. Here’s an example of one
public class AppInitializer { public static IApp StartApp(Platform platform) { if(platform == Platform.Android) { return ConfigureApp .Android .InstalledApp("com.putridparrot.MyApp") .PreferIdeSettings() .EnableLocalScreenshots() .StartApp(); } return ConfigureApp .iOS .StartApp(); }
Test files (again the project template will include a Tests.cs file) might look like this
[TestFixture(Platform.Android)] [TestFixture(Platform.iOS)] public class Tests { private IApp _app; private readonly Platform _platform; private static readonly Func<AppQuery, AppQuery> LoginButton = c => c.Marked("Login"); public Tests(Platform platform) { _platform = platform; } [SetUp] public void BeforeEachTest() { _app = AppInitializer.StartApp(_platform); } [Test] public void DoesNothing() { // Arrange // Act // Assert } }
Writing UI tests is much the same as writing standard unit tests, we mark our test methods with the NUnit TestAttribute, then Arrange, Act and Assert our UI. For example let’s assume we have a button with an AutomationId Login then we can create a static field within our Test class like this
private static readonly Func<AppQuery, AppQuery> LoginButton = c => c.Marked("Login");
This function can be used within various _app methods, such as _app.Tap(LoginButton).
As you’ve guessed I’m testing a login UI, so assuming we have a login screen with username, password and a login button we might setup our tests like this…
First Arrange our UI, for example check that we’re on the correct screen and we’ve entered a username and password. Then we’d Act by tapping the Login button then finally Assert by checking we’ve moved on from the Login screen.
REPL
When we run our tests we might want to run the _app.Repl() method which will display a REPL command line, this allows us to view the tree representing our UI.
References
Cannot run XAMARIN UI TEST on xamarin.forms, error System.Exception