I recently completed a client engagement that involved building a suite of automated user interface tests for a critical internal web application. The web application has existed for several years and is built to work with Internet Explorer (the client’s SOE browser) and the client has also invested heavily in the Microsoft ALM stack including Team Foundation Server, Test Manager, and some advanced capabilities of Lab Management. Given this background, Coded UI was a suitable technology choice for driving the user interface from tests.
Initially my team and I wanted to take a code-first approach to Coded UI, something similar to the Page Object pattern commonly used in conjunction with Selenium-based testing. However, given that the test team would be responsible for maintaining the test suite, not the developers, the client pushed for us to build a solution using the Coded UI Test Builder tool to record the tests interactively. From experience I’ve found that record-and-replay testing approaches tend to be fragile and difficult to maintain so we found a compromise, a hybrid solution.
The design involved having a single UIMap (“.uitest” file) corresponding to each of the individual pages (or popup windows) in the application. Within each UIMap, we recorded discrete, almost atomic actions, as individual methods (eg “enter text in search box” and “click search button” would be separate methods, recorded separately) and similarly with asserts. We would then hand-code the actual test methods as as series of calls to each of the actions and asserts on the relevant UI Maps, interspersed with control flow code as needed. As an intentional by-product of naming our UIMaps and methods clearly, the hand-coded test methods read very similarly to original manual steps described in each Test Case. Coincidentally, a lot of this approach aligns with the recommended practices.
With this hybrid approach, whenever a part of the web application changes, instead of needing to re-record large chunks of actions and tests, only the individual affected pieces need to be updated. The problem with the Coded UI recoding model in general though is dealing with HTML Tables and the application we were testing used Tables to display grids of data and for selecting data on almost every page. When recording actions against a Table with the Coded UI Test Builder it results in code that will later match a cell based on its row and column index or its cell content, or both. This is fine for single-cell actions and assertions but when you need to locate, for example, the cell in the first column containing a specific account number and then click the corresponding cell in the third column on the same row, Coded UI recordings don’t help. In fact, the hand-coded equivalent of this scenario using the Coded UI APIs directly isn’t great either.
As we progressed through the test cases, we realised the Table scenario needed a good solution. We tried three different approaches over the course of the project and improved incrementally on each based on what we learned. In the end we had a strongly-typed table abstraction that kept the test methods clean and the UIMaps maintainable. The Coded UI Test Builder was still used to record to selection of the HTML <table> element within the page, and we kept the column mapping simple in the UIMap partial class, so we didn’t prevent the testers from easily updating the tests with the application.
I have published the table abstraction code we used on GitHub, along with a basic DemoWeb application and an accompanying Coded UI test to demonstrate how we use it. Here is the Page Object pattern in use where a particular row structure is defined along with some domain-specific Find and Assert methods.