rothwell.im

by Jonathan Rothwell

Stupid Selenium WebDriver tricks

Selenium sounds like the name of a suicide drug. Naturally, the reality is much, much worse. It’s an automated testing environment for web applications, and wrestling with it on a daily basis in your line of work is a complete pain in the neck.

In case it’s not already abundantly clear, I have grown to detest Selenium with a passion. Nonetheless, automated testing must be done, and if you have no choice, you may have grin and bear it as you get intimately acquainted with WebDriver.

Automated testing is far from my forte, but in the two and a bit months I’ve been working at ServiceNow I’ve picked up a few stupid tricks1 whilst stumbling over the typical newbie pitfalls. I’m documenting them here in a semi-continuing series for my own convenience (in anticipation that I will inevitably forget them) and also in the hope that they might be useful to someone else, or hint hint that someone else will correct me on something I’ve done horribly wrong.

Best practice: identify ALL the things; avoid XPaths

As a rule of thumb, any code written for a new product in the first years of a startup will be terribly-documented, and hard to test. When you’re working on a large codebase, widely-used and widely-developed on, the excuses begin to dry up for these kinds of practices. I know that parroting on with bites like comment your code! Make it readable! should go without saying, but just as important is making your code testable.

Selenium has several ways of referencing elements on the page. The best way to do this is by its id, name or class attribute (in descending order.) Failing any of these, you’ll probably end up using an XPath.

This is what an XPath looks like. (It’s a reasonably simple example: the ‘submit’ button from the reddit home page.)

/x:html/x:body/x:div[2]/x:div[5]/x:div/x:div/x:a

If this looks scary, imagine how bad it gets when you’re dealing with nested divs, iframes, AJAX and dynamic content everywhere. It quickly becomes a real mess.

This is why it’s important to, wherever you can, give predictable id or name attributes to your elements. For example, in ServiceNow, every record is given a unique identifier called a sys_id. We recently found ourselves wanting to create a checkbox for every item in a table. Our generated code ended up looking something like this:

<input type="checkbox" value="My Item" name="5d61d0104ae146569327d76fac679bd4" />
<input type="checkbox" value="My Other Item" name="7ad5bc8d3e9b4bdf883e16f9bc1dd2f1" />

This meant we could address it, when writing our auto-tests, like this:

// assuming theDriver is our Selenium WebDriver
// and we've assigned the sys ids in a constants file
WebElement theFirstItem = theDriver.findElement(By.name(MY_ITEM_SYS_ID));
Assert.assertTrue(theFirstItem.isSelected());
WebElement theOtherItem = theDriver.findElement(By.name(MY_OTHER_ITEM_SYS_ID));
Assert.assertTrue(theOtherItem.isSelected());

There are two advantages to doing it this way.

  1. XPaths are horrifically ugly things to look at in code. Admittedly, so are GUIDs, but I’m of the opinion that any sensible name be substantially more readable than an XPath.
  2. XPaths break easily. If another div is added, for instance, or some ordering is changed (which does happen in web applications) the XPath will break.

I dislike using XPaths for testing and personally avoid them like the plague. Maybe I’m doing it completely wrong (this is not at all unlikely.) If you can provide a compelling reason for using XPaths, do let me know. Until then, however, I will avoid using XPaths in my tests at all costs, and advise you to do the same.

Pop-up windows

Despite their passé association with the drive-by-downloads and sleazy injected spyware-disguised-as-smileys ads from yesteryear, there will probably be times when you’ll want to test popup windows.

Switching between windows in Selenium should be easy. Often, it’s made harder than it should be. The best way I’ve found to do it, janky as it is, is:

  1. Before you do any switching, store the handle of the base window;
  2. Open the popup window;
  3. Get the handles of all the windows;
  4. Remove the base window by process of elimination and switch to the popup.

In practice, this looks like this:

String theBaseWindow = theDriver.getWindowHandle(); // we need this to switch back (as before)
Set<String> allTheWindows = theDriver.getWindowHandles(); // note the plural!
allTheWindows.remove(theBaseWindow);
String popupHandle = allTheWindows.iterator().next(); // process of elimination
theDriver.switchTo().window(popupHandle);
// do your testing
theDriver.close();
theDriver.switchTo().window(theBaseWindow); // return to the original window

This isn’t a very elegant way of doing it. I willingly invite other suggestions: please email me if you have a better idea. As I said, I expect this to be a continuing series as I carry on slogging through life with WebDriver, so your opinion is appreciated!

  1. Note that the tricks I’ll be documenting here will work anywhere you use WebDriver, and aren’t ServiceNow specific. Within ServiceNow we have our own conventions for, and extensions to, WebDriver. Naturally I won’t be documenting these here.