Wednesday, 29 April 2009

Selenium CSS locators

We've got a project with quite a large set of tests, and we have both Firefox and IE7 continuous builds using selenium. The annoying thing is that something that takes 5 minutes in Firefox can take 30+ minutes with IE (I kid you not!).

It turns out this is a common problem and is due to the lack for native xpath support in IE. So for all the selenium tests that use xpath=//..... in IE the xpath is actually being evaluated using javascript ... ouch. An alternative is to use css locators, you can't do this everywhere but as well as improving the IE performance it can also provide some rather tidy rules. For example


xpath=//div[contains(@class,'balance')]
or
css=.balance

xpath=//div[@id,'topLeft')//span[contains(@class,'name')]
or
css=#topLeft .name



So even if you dont' care about IE using css selectors might be a lot nicer. (It might also improve your css skills!)

7 comments:

Rob said...

This is really great. The only problem I've had trying to use it is when selecting a particular element from a set that match the rule (e.g. dealing with list items or table rows when you're trying to target a particular one). Nothing is more horrible to maintain or more prone to break due to minor front end changes than overcomplex XPath locators.

Targetting table elements using verifyTable is a similarly powerful technique.

Rob said...

It's worth noting that you can still use (verify|waitFor)Attribute with css selectors. e.g.

verifyAttribute #someContainer a.someLinkClass@href http://icanhascheezburger.com/

Agile Enforcer said...

Ah, cool. I didn't realise you could access attributes like that.

Dan said...

It's also worth noting that the examples given are not entirely comparing apples with pears.

Take this XPath:

xpath=//div[contains(@class,'balance')]

If the reason behind the 'div' is actually to confirm that the element is a DIV, then this would be the correct CSS locator syntax:

css=div.balance

Although having said that, the 'contains' function of the XPath engine would also match substrings of class names, so a DIV with a class of 'unbalanced' would match the XPath rule but not the CSS rule.

If you want to do explicit class name matching with an XPath, one way (there are probably many) would be:

xpath=//div[contains(concat(' ', @class, ' '), ' balance ')]

Glenn Saqui said...

Dan that's a really good point that I hadn't really thought about before. Thanks for the heads up it will keep me from making a bonehead mistake in the future.

Agile Enforcer said...

I also managed to get specific element selection working with

css=#someContainer p:nth-child(2)

I can't remember whether we tried this with IE and found it didn't work, however it seems to work with firefox.

It's not quite as clean as you might like but I've found it usually looks better than the equvalent xpath.

Agile Enforcer said...

One more reason to encourage css rather than xpath locators is that working with css locators will also help your css work, whereas xpath locators are only good for writing selenium tests (or parsing xml documents ... shudder),