Welcome to my blog series on cross-platform UI testing. In Part 1 of this series, I discussed my strategy for cross-platform testing and identified a few key gotcha's to look out for along the way. I also provided a link to the completed source code, should you like to follow along or find something specific. Today we will go over implementing the initial project boiler plate. Windows requires the most work, so let's get going!
Platform Specific Implementations
At this point, we are basically going to follow along with Rob Gibbens' excellent example project and implement his patterns, but we're going to do some minor refactoring and use some OOP to share across all the platforms. Following along the tutorial we will:
- Create test library projects for Windows and Xamarin
- Add the custom SpecFlow generator plugin to the windows project
- Configure SpecFlow in the App.config
- Create an IApp implementation for Windows
- Modify the App Initializer classes for Xamarin and Windows
- "Add Test Apps" to the startup for iOS and Android
- Create concrete implementations of the abstract FeatureBase and StepBase classes, and decorate them for the test framework.
- Define an interface for each screen that identifies the controls/objects we want to test
- Create a concrete implementation for each platform that binds the control's name/id
- Expand Rob's excellent work managing the iPhone Simulator to do the same for Android's Emulator
- Create our first feature files and steps. (more on this later)
Set up CodedUI and Xamarin.UITest Class Libraries
Now we are going to create two new class libraries. Create a new class library for CodedUI using the template installed in VS. This will bring in all the necessary reference .dll's and save you the headache of manually doing it later. Now just like the Shared library, grab the SpecFlow and Xamarin.UITest nuget packages. Also add a reference to the Shared library we created. You can also create a Xamarin.UITest project now as well.
Now you need to configure the UITest.Windows project to use MSTest and to reference the custom generator we built following SpecFlow' docs. That's done in the App.config. You’ll notice in the source I compiled the project one time and then moved the .dll to the packages folder. I did this because the file doesn’t really change, but also because I have seen the generation fail in VS if this .dll is recompiled over and over again. You might also notice that the Folder path is Windows.Generator while the assembly name is TipCalc.UITest.WindowsCodedUI.SpecFlowPlugin.dll. This is because SpecFlow is particular about its naming conventions, and I wanted to make this distinction.
Create the Windows IApp Implementation
Create a “WindowsApp” class and inherit the Xamarin.UITest.IApp interface. You’ll notice that this is quite a large interface. It’s time to create an implementation of IApp for windows, so we can reference it in CreateApp. Go ahead and define that class, and implement the Xamarin.UITest.IApp interface. You'll notice this is quite a large interface, and we won’t be implementing the whole thing. We will be using the CodedUI Test Gesture API’s to simulate interaction with the application on screen. But first, we need to tell the test suite to start the application.
For now, let’s define a XamlWindow and initialize it in a new "StartApp()" method:
The app Id is the fully qualified windows app Id in the format of:
You'll need to PowerShell your way to this value since there's no other easy way to get it. I have a post-build script that runs and includes something like this:
$packageName is the packagename prefix of your appId
Ok, let’s look through this interface and see what we need to implement. Under the hood, Xamarin.UITest is using a query syntax to give you a nice functional-style syntax for interacting with the application. I didn’t have time to implement that on windows; instead, you will notice that most of the API methods have overloads that take a string called “marked”. That corresponds to the friendly name of each UI element. If you have a screen called “TipView” and on it is a textbox with the Id “Subtotal”, you should pass in the control Id to find it on the screen.
To emulate this behavior quickly, I’m going to create a dictionary of screens and then loop through them using the unique ID supplied. It is important that your Id keys in your app are unique if you want to put them under test like this. FindControl is an extension method in the TestExtensions.cs class that recursively searches the XAML tree for the control.
Now we have a means to start the IApp, and a means to find a control we are interested in. But we still need a way to interact with the application. As part of Microsoft’s recent push toward tablet and touch based UI’s, they added Test Gesture support to CodedUI which makes all of this possible. With gestures like Tap and PressAndHold, it would be fairly straightforward to implement the entire Xamarin.UITest.IApp interface using this method. For now, we are just going to implement EnterText using the Keyboard class.
Create the Windows AppInitializer Class
When you created your UITest.Xamarin project, you probably noticed it came with an AppInitializer.cs that is pregenerated. We are going to implement this on Windows to manage the application lifecycle. The key method StartApp uses a fluent interface to start the application.
Here we are creating a new instance of our WindowsApp, associating it to the proper AppxPackageId, initializing CodedUI playback, and starting the application.
Great! Now we have a way to start up our Windows application in a cross-platform way. Using Xamarin.UITest's fluent interface we are able to activatea a windows application and perform basic operations on the window. Join us next time for Part 3 when we will start creating SpecFlow Features and Steps!
Follow me on twitter @AddressXception