In this post I share a UI test framework design that’s worked well for our automation team. This framework design strives to adhere to the don’t repeat yourself (DRY) principle from the book The Pragmatic Programmer and I think this is reflected in the way we structured the solution.
I created a little C# test project to demonstrate the simplicity of the design. It contains a single test method that opens a Chrome browser, navigates to the Yahoo search page, enters a search term, and asserts an expected search result.
Create the Projects
This solution was created in Visual Studio 2013 and contains two projects:
- Tests (a Unit Tests project)
- PageMaps (a Class Library project)
Also needed are libraries installed via the Nuget Package Manager which should be added to both projects after you create them:
To start the first project, open Visual Studio and create a new Unit Test Project named “Tests”:
Add a second project to the solution, a class library project called “PageMaps”:
Create the First Page Map
The big idea is to keep the details of each web page interaction in its own class and separate from the test logic. This allows some real advantages.
The first advantage is that the page element navigation can be defined once then reused by any test. If the UI changes, which it always does, the code update is needed only in one location, the page map file. The second advantage is the test method itself can be kept clean and short.
Create a resources file to store the element Xpaths
The Selenium WebDriver component can locate the page elements using several methods, by element Id, XPath, Css, and several others. I use XPath because it is reliable, doesn’t rely on visible text, and you get the rich Xpath syntax if you want to customize the XPath returned by the Chrome developer tools; yes, Chrome does the work for you.
I like to store my Xpath strings in one place for easy reviewing and updates. A resources (.resx) file is a great place for this. Highlight the PageMaps project in the Solution explorer, right-click the name and select Properties. Select the Resources tab and click on the link shown to create a default resources file.
To get the first element XPath for our sample which automates the Yahoo search page, open Chrome to http://search.yahoo.com, right-click the Search button, and click “Inspect Element”.
This opens the Chrome Developer Tools sidebar with the <input> element highlighted. Right-click the highlighted element and click “Copy XPath”. Create a string value in the resources file for each needed page element XPath expression as shown below.
Now we’re ready to write some code!
Search Page Map
Add a new class file to the PageMaps project called SearchPage. We’ll add three properties to wrap the Selenium selectors, one each for the search page text input, the search button, and the search results area. Type the following code into your SearchPage class. You’ll need to add a “using OpenQA.Selenium” statement to the top of the file.
We need a method in our SearchPage file that takes a search term string and validates whether the page displays expected text. If the test succeeds, a reference to our map page is returned, else an Assert exception is thrown.
Add a Test Method
Now we’re ready to create our first test. In the UnitTest1.cs file, which I renamed to SearchTests.cs, change the TestMethod1 name to “BasicSearchShowsWikipediaEntry” and add the statement shown.
This test may look a little strange if you’re used to the Arrange–Act–Assert style. After some back and forth discussion our team settled on this style, aka “fluent”, wherein each method returns the appropriate type of object for the next method. For more discussion of the fluent style, check out this post by Tom Fischer, Fluent Code in C#.
In this framework, the SubmitSearch method enters the search term, clicks the submit button, asserts the text is found on the page, and returns a reference to the search page. While the reference isn’t used in this example, a little imagination can enable you to visualize how a multi-step scenario could be done in a single statement by chaining together methods.
For example, imagine for some reason we wanted to test whether 5 consecutive searches all returned results containing a Wikipedia entry. We could write our test like this:
This is a contrived example but imagine you needed to visit several pages in sequence, and each action method returned the landing page to perform the next step. All the action details and assertions are contained on the map page and the test becomes merely a sequence of steps that can be chained together or tested separately in their own test method.
We’ve been using this “fluent” style of writing test automation for almost a year and it’s been very efficient. Using a resource file for element paths and having a separate page map for each page under test has also proved its worth. You could extend this example in several ways, such as creating a base map page containing common elements, such as navigation controls, which is inherited by all page maps.
I hope you found these concepts useful.