djangoproject.com | python.org | linux.com
demongin.org - More Resilient and Scalable Selenium Tests in Python with Keyboard Shortcuts

More Resilient and Scalable Selenium Tests in Python with Keyboard Shortcuts

A short post on writing better test scripts in python for use with your Selenium RC server.


Wednesday, 2010-01-06 | Careerism, Programming, Testing

"Now, whether he kill Cassio, or Cassio him, or each do kill the other, every way makes my game."

Othello, the Moor of Venice

So, long story short, now that the end-of-year rush is over at the day job, I was able to get back to work on my big testing suite project, only to find that the developers had fiddled with the UI and a bunch of my tests were error-ing out.

D'oh!

It's double d'oh, actually, since I realized that this was kind of my fault. After all, one of the cardinal rules of software development (I've been learning), is that when your software doesn't scale, it really is your own fault and nobody else's.

So, in order to write better, more scalable and resilient tests, I found some places where locator ID information, which is what ended up being the volatile element, could be replaced with keyboard shortcuts. Take, for example, my simple test of basic search functionality. A single search test, which enters a term and then searches the resulting page for the expected results (finally handing the results off to another function, which checks them for accuracy), looked like this:

    def search_function(self, sel):
        sel.type("searchbox:search", "SEARCH_TERM")
        sel.click("searchbox:_id38")
        sel.wait_for_page_to_load(sel_timeout)
        return sel.is_text_present("EXPECTED RESULT"), sel 
The thing that I realized, while I was thinking about how to make these tests scale a little better, was that the part where I call the click function and use the locator "searchbox:_id38" was definitely the weakest link: any time the engineers add or subtract a searchbox element, that id number changes and my test either errors out or fails, presenting a false positive.

What I realized, when I was thinking about how to fix this, was that if I were to test out this UI funcitonality manually, I would click on the box, enter my terms and then TAB over to the submit button and hit ENTER (or SPACE). And so I re-wrote the test to do just that:
    def search_function(self, sel):
        sel.type("searchbox:search", "SEARCH_TERM")
        sel.key_press("searchbox:search", "\\9")
        sel.key_press("searchbox:search", "\\13")
        sel.wait_for_page_to_load(sel_timeout)
        return sel.is_text_present("EXPECTED RESULT"), sel 
Using the very generic key_press function and the ascii value for the keys I wanted to press, I was able to simulate tabbing over to the button and submitting without a click. This works because of how Selenium works: since the program is simulating an actual user's interactions, the fact that the type function comes before the key_press function means that the "cursor" is actually in the search input box; pressing TAB, therefore, tabs to the next tab-selectable element on the page.

Now, obviously, this isn't bullet-proof: if my engineers go and move things around again, such that the search input box doesn't appear right before the submit button, the whole scheme is toast. But, on the other hand, in this case (and several others like it), if that aspect of the UI were to change, then something would definitely be wrong with the UI from a QA perspective.

And, as Iago famously quipped, "every way makes my game."