For those who don’t like reading long posts: I have created a Fluent API for automated UI testing based on components in the System.Windows.Automation namespace that ship with .NET 3.0. I have posted the early code for it on GitHub at https://github.com/MikeHanson/WATKit. It will be fully documented on the GitHub Wiki and I will post on it here. If you want to know how I got to this point and why I am creating another UI Automation API read on.
After an aborted attempt to retire early I am back in a new contract in central London working as part of an agile team on a WPF fat client application. As is common in agile teams developers have to code acceptance tests that automate the UI. As earlier posts demonstrate I have some experience of this with web apps and Silverlight but I had never had to do it with a WPF app. Coincidentally I have started work on a desktop client for a community site I run at www.vrcyclist.com and I am doing so using BDD with SpecFlow. Ranorex is the platform of choice at work, but this a commercial product that is outside of my budget, so I started looking at free/open source WPF automation options in my spare time.
Since v3.0 the .NET Framework has included an automation framework in the System.Windows.Automation namespace. This is all packaged in a set of assemblies in C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0 beginning with UIAutomation. These are not specifically for automated UI testing but make it possible. They aren’t difficult to use but the code is quite verbose and almost immediately I wanted a more developer friendly API. I already knew of an open source project on Codeplex called White that I had reviewed for use with Silverlight. I looked for others but couldn’t find anything that wasn’t commercial. So I downloaded White and started wiring it into my acceptance tests.
As far as launching my application and finding windows and controls went it worked fine, and looked promising, but, my very first really simple attempt to create a CustomUIItem failed miserably. If you have read any of my articles on WebAii you will know I recommend creating wrappers for windows, views and controls in your application and have your tests drive these wrappers rather than finding individual elements repeatedly. The CustomUIItem base class is mean to be the way to do this with White. I followed a simple example in the documentation to create a wrapper for a WPF ValidationSummary control I had created, it should have worked but White could not find my control. I posted a question on the White Codeplex site and got a couple of responses from the original author. Google turned up a number of people with the same problem and offering workarounds but to be honest I didn’t see the point of using White for the workarounds you could achieve the same thing without the CustomUIItem base class. Anyway not being a patient person, by the time I got a response to my post on Codeplex I had already started writing my own little API to make working with the Microsoft UI Automation framework easier, mostly a set of extension methods that made code less verbose (the route a number of the Google results indicated others had taken).
As indicated by my previous post I have become a fan of Fluent APIs like Fluent Assertions and at work I am using Fluent NHibernate for the first time. As I developed my API I started seeing the evolution of a Fluent Automation API and the more I did the more I liked it. Combined with Fluent Assertions my test code was reading like a Story and the more I saw this the more I liked it and have spent a significant amount of time in the last few weeks re-factoring what I had started into a more complete Fluent Automation API and will continue to do so until it is fully usable. At first I was doubting the sense of creating another API rather than working to figure out the kinks in White, but the fluent aspect of the API changed this as I am fairly sure it makes it unique (at least amongst public offerings) and I am really enjoying working on it at the moment, so will stick with it to completion.
Enough of the blabbering, here are some tasters of what my Fluent Automation API looks like:
1: var uat = Fluently.Launch("C:\MyApp.exe")
2: .WaitUntilMainWindowIsLoaded()
3: .WithDefaultMainWindow();
This is the start point and launches the application under test returning the default wrapper for the main window of the application.
1: var uat = Fluently.Launch("C:\MyApp.exe")
2: .WaitUntilMainWindowIsLoaded()
3: .WithMainWindowAs<MyMainWindow>();
This is my preferred alternative that allows you to specify that a strongly typed wrapper is used for the main window.
1: var button = aut.MainWindow
2: .FindControl()
3: .WithId("MyButton")
4: .IncludeDescendants()
5: .Now()
6: .As<Button>();
Having launched your application you can use the MainWindow property to start finding elements. This example uses .As<Button> to return a strongly typed wrapper that is included in the API. You can also use AsDefault() to return the element as a base AutomationControl. If the button is not actually found you get a proxy that can be used to repeat the find or execute a Wait on.
1: button.Wait()
2: .UntilExists()
3: .TimeoutAfter(TimeSpan.FromSeconds(5), true);
This is how you would get a proxy to wait for the button to exist. The second argument to TimeOutAfter indicates an exception should be thrown on timeout. The default is not to throw an exception but you can check the state of the button to identify if it is still a proxy or the real thing.
Well that is enough for now, let me know what you think and feel free to contribute ideas and comments here or on GitHub at https://github.com/MikeHanson/WATKit
NB: After I started this post Telerik posted an update to the WebAii framework that included WPF support. I took a quick look and it, and whilst it is a welcome addition the fluent aspect of my API is still unique so I will be sticking with it.