This post will continue the previous post of Scaling up your CUIT UI automation for real world projects and walkthrough a sample test project using multiple UI maps.
For this walkthrough, I illustrate writing tests for an e-business website called TailSpin Toys that is an asp.net web application. The application is a regular shopping site that has different model planes for sale and users can login, view model plane details and purchase those that they are interested in.
The test project testing it contains 11 UI maps, each for different portions of the GUI. The test project is available for download here. I suggest readers download the code and look through as they read along the doc.
Since the application has several web pages and a number of user scenarios going through each of those pages, we structure our test project like this:
- One UI map per each UI construct that can be treated separately. Eg: billing page, order details page, shopping cart etc.
- Corresponding .cs and .designer.cs files for each UI map
- One class called TestRunner in the testrunner.cs file that acts as a single point interface to all the test methods and contains instantiations of the UI maps
- Tailspintestcase.cs file containing the test methods for each of the tests
The approach to writing test automation in this case will be slightly different from the one we use in our current Coded UI default model where you use the builder to record actions and add validations to compose the test. In this case, we will first create the building blocks in the form of different UI maps and then use those to compose a test method that acts on each of the different UI blocks.
To construct a UI map, just add a new Coded UI map from the “Add New Item” menu and open the coded UI test builder. Now, record actions on the desired UI or drag and drop controls using the cross hair. Once you are done with the operations on that particular UI component, generate code and save. As you navigate to a new UI component, again create a new UI map and load it into Coded UI builder and repeat the same process. In the Tailspin example, I create a UI map called “HomePage” and drag and drop controls like the different hyperlinks for plane options, hyperlink for cart etc. You can either record a set of actions or compose an action in the HomePage.cs file by creating a new method and adding actions on the controls captured inside the method. I’ve added a couple of navigate methods in the homepage UI map .cs file.
Once we have built all UI maps necessary for a scenario, we are now ready to start testing. In this case, my test scenario is a simple one – buy the “Contoso” Paper Model airplane in the site and ensure order receipt is delivered post billing. You can break this down into the following steps:
- Launch TailSpin
- Clean Up existing shopping cart items
- Go to “Paper Model” page
- Select “Contoso” plane
- Add it to the Shopping cart
- Go to shopping cart and proceed to check out
- Fill out billing and shipping details
- Verify you have order receipt
This scenario has to act on controls from different UI maps. Therefore, we need to instantiate the UI map objects for each of the controls to act on. To make the test method readable and easily maintainable, we add a utility class called test runner that contains properties per each UI map in the test project. When you add a new UI map, you just need to go add a new property in this class to access the instance of the UI map. In the test runner class, we add methods that are thin wrappers over the UI map’s methods containing the recorded actions. Eg: NavigateTopaperModel, SelectaPlane etc.
Now, in order to run this scenario, the test method only has to access the common utility class:
Likewise, we can compose more tests in different test classes, instantiate the utility class and act on different UI maps via the utility class object. Note that you could have very well skipped creating the utility class and directly instantiated each of the UI map objects in every test method and called them directly. That approach will also work, but the test code may look a bit more cluttered. Functionally, however the 2 approaches are equivalent.
Note that since we are testing a web application, the top level window, which will the browser displaying this web site, will be the common ancestor for all controls in the web site. Therefore, we will have at least one instance of the top level window control per each UI map. This leads to duplication of controls across maps, since there are 11 instances of the same top level window across the test project. To fix this, we use the CopyFrom() API to copy the TLW from the HomePage’s UI map into the TLW of each of the new UI maps. This API is called in each of the properties returning UI map instances so that during construction of the new UI map object, we equate the TLWs across all UI maps.
For a data driven test method, the only change would be to pass the desired values as parameters to the calling function in the utility class (which will in turn set the values in the parameters of the object passed into the method defined in the UI map) or directly pass to the UI Map objects themselves. For eg: