Part of what I’ll be blogging about over the next few months is my journey to become an expert with Test Studio. I’m not an expert yet, but I do bring a long history of applying tooling and frameworks to solving real-world problems, and I’m going to be working through many of those same problems in short order—figuring out how to solve those same problems is an awesome way to learn a new tool or framework’s strengths and weaknesses. I hope that by sharing my discoveries I’ll help you work through some of the same common problems!
First up in this informal series: dealing with test-hostile UIs.
If you’ve been around web UI test automation for any length of time then you’ve likely had to deal with UIs that are unfriendly, if not outright hostile, to automation.
If you’re lucky you’ve been able to work with a clean Document Object Model (DOM) where you’ve got easy, clear element locators to work with. (By “element locators” I mean good ID values, CSS classes, or relatively simple XPaths.) These friendly locators make it simple to write precise tests which are, over time, more maintainable. If you’re lucky your DOM is well-structured and has some semantic context to it, too. Hopefully you’re also able to avoid table-driven layouts which can greatly drive up a DOM’s complexity, too. (Tables are fine for tabular data, just not for general layout.)
NOTE: If you’re unfamiliar with the basics of a DOM, I highly recommend the W3School’s DOM tutorial. It’s a terrific resource for learning more about the DOM.
Unfortunately, many folks have to suffer with DOMs that aren’t so helpful. I’ve had far too many occasions where I’ve had to deal with some or all of the following:
There are a huge number of factors for why DOMs get to this state, but this is the reality for many folks writing automation.
With that in mind, let’s look at a problem I’ve had to solve a number of times: diving in to a web page to crack open a way to validate content inside an unhelpful DOM.
Here’s an example of a page similar to what I’ve worked with in the past:
With my awesometastic ninja CSS skills the rendered page looks something like this:
The premise of this page is that it may be pulling the news items from a database or aggregating from several different sources. To test this system I’ll need to validate a number of things around the content being pulled in and rendered.
One of my tests might be validating the number of news items displayed in the main content area of the newsletter. If we’re using a baseline dataset, I should see an expected number of items in the newsletter’s main panel. A second test might be validating that the titles of the news items in that panel should also be from an expected set.
In my layout, the news items are <div> elements decorated with the CSS class “NewsItem”. They are contained in a <td> table cell within the NewsLetter ID’d <table>. Note there are no useful ID attributes in any of those elements…
Test One: Counting NewsItem Elements
Depending on the overall UI, I can’t simply count the number of items using the NewsItem CSS class – there may be other items elsewhere on the page which also use that style. Ergo, I need to be very specific with my locators: I need to first narrow down to only the NewsItems within the specific MainContentArea <td> cell of the table.
There are two different approaches I can use to nail this down: XPath, and a good Find Expression in Test Studio or the WebAii framework. I’ll start with XPath.
There are a number of tools you can use to figure out exactly what XPath identifies the NewsItems elements. In the interest of time and space, I’ll not go through this here, but simply point you to a blog post I wrote a couple years ago which walks you through this exercise using Selenium IDE, Firebug and XPath Checker.
The XPath to locate all NewsItem elements within the MainContentPanel is this:
Now for the actual work in Test Studio. I’m skipping project and test setup and am focusing only on getting this portion of the test working. We’ll need to use a Coded or Script test step. Insert one via the UI:
Edit the newly inserted step by right-clicking on it and selecting “View Code.”
So back to the test: I need to get a collection of the NewsItem elements in the content area and count them.
Two simple statements of code let me get the job done:
The Find class gives us a number of extremely handy methods for locating elements – and with the ability to use a wide range of ways to locate those elements. You can use IDs, CSS classes, nodes, and yes, XPath. The Find.AllBy* methods return collections. If you’re looking for a single element then you can just use the Find.By* equivalent. (See the Testing Framework API Reference help file in Test Studio’s documentation directory for a full listing of the framework’s functionality.)
Note: I’ve hardwired the XPath locator into this code example. Locators shouldn’t ever be stored in your test code – they should stored off in Test Studio’s Element Repository where appropriate, or in the equivalent of a Page Object, dictionary, or other single source. I’ll be writing and speaking about maintainable automation extensively, and I’ll always call out where I’m breaking my own rules for brevity’s sake in examples.
We’re not locked in to XPath for solving this particular problem. XPath won’t ever be confused with an easy-to-understand way to locate elements in your DOM. Moreover, some versions of Internet Explorer have serious performance issues with resolving XPath.
Instead of XPath we can look to the test framework’s own find by expression functionality. We can replace the two statements above with these:
The first statement returns us a strongly typed HtmlTableCell found in the DOM matching the class MainContentArea. We use that to chain the next Find – we’ve limited the scope of the DOM to only that particular table cell – so the Find.AllByExpression gets us a collection of NewsItem-decorated <div> elements in that cell.
This form of chaining gives us great power for crafting very powerful, flexible expressions since you’ve access to the entire element’s contents and DOM subset.
With this first test out of the way, let’s move on to the second one.
Test Two: Validating the Items’ Titles
Again, we’ve the premise I’m using a baseline dataset which I can base my expectations on. With that in mind, I need to again get a collection of the NewsItems in the MainContentArea, then iterate through that collection and compare the titles against expectations.
An XPath-based solution could look like this:
This works just like the previous test: use Find to get a collection based off an XPath. We then have to use a quick foreach to iterate through the list and ensure each item is contained in our list of expected titles. We have to do this because Enumerable.SequenceEqual (implemented by IList) compares contents in the same order for both instances. Maybe our news items don’t always come through in the same order. (That’s another test.)
If you do a lot of work comparing lists, you might consider writing your own extension methods to handle these comparisons. NUnit, another popular testing framework, has some great functionality in its CollectionAssert methods.
Here’s how it looks using the WebAii framework’s Expressions:
Wrapping It All Up
Working with test-hostile DOMs is a matter of fact for some test automation folks. If you’re saddled with this sort of DOM, I encourage you to try and work with your UI developers to gradually clean up some of the problems. Get them helping you to write a few automation tests so they see the difficulties the complex DOM is causing. They may be able to help you out with some adjustments to the system.
If the system can’t be adjusted, then you can continue to fall back on XPath or the Find Expressions to help you navigate complex DOMs and get the appropriate level of maintainable tests in place.
Please feel free to pass on your own experiences or concerns, either in the blog’s comments or directly to me via e-mail: firstname.lastname@example.org. I love hearing the issues folks are facing in the real world!
Subscribe to be the first to get our expert-written articles and tutorials for developers!
All fields are required