I always liked using XPath for locators in Selenium and WebDriver. It was more procedural than using Cascading Style Sheets (CSS) and felt more like programming. However, using CSS for locators is much faster than using XPath. I haven't done any tests to see just how much faster but it is significant enough that I can see the difference.
I run a test suite using XPath locators. I can see significant delays where there appears to be nothing happening on the screen. I refactor the code to use CSS and the delays disappear. I would estimate that 3 to 5 second delays (XPath) become too small to measure with the naked eye (CSS).
The only feature I have been unable to do in CSS are locators which use the XPath text() function. CSS seems to be all about the tag and its attributes but not the text contained within the tag.
The one thing it took me a little while to get used to when switching from XPath to CSS was compound statements. For example, I might have an XPath like:
"//div[contains(@class,'prefspanel')]/table[@id='sizer1']"
So how do I convert this to CSS?
"div[class~='prefspanel']>table#sizer1"
Shorter and pretty much the same attributes. The ~= isn't quite the same as the contains() function. If I have:
class='prefspanel foo bar'
the ~= will find prefspanel because it is a 'word' separated by whitespace. The contains() function will find 'prefs', 'prefspan', 'prefspanel' and even 'panel' because these are all substrings of the original. In CSS3 they introduced *= which is just like contains().
I also notice that many attributes will change dynamically. However, the start of the attribute or the end of the attribute will be consistent. In XPath I have to use the contains() function to find the attributes which end with the unique identifier. With CSS I can use ^= to see if the attribute begins with a substring or $= to see if the attribute ends with a substring.
One other place that XPath seems to work better is when you are reversing up the DOM. For example, if you have the following:
<div id='pair'>
<span>
<p>Label:</p>
<input size='25' type='text' value=''/>
</span>
</div>
I would typically find the input using:
"//p[contains(text(),'Label:')]/../input"
There is no easy way to do the same thing with CSS. With CSS I would find the input from the div tag. However, I usually find the p and input are paired together and the XPath always works but the input relative to the div changes occasionally.
Google Analytics
Search
To search for specific articles you can use advanced Google features. Go to www.google.com and enter "site:darrellgrainger.blogspot.com" before your search terms, e.g. site:darrellgrainger.blogspot.com CSS selectors will search for "CSS selectors" but only on my site. |
Showing posts with label CSS. Show all posts
Showing posts with label CSS. Show all posts
Tuesday, January 31, 2012
Wednesday, December 28, 2011
Selecting WebDriver locators
When you are using WebDriver for test automation a test case really boils down to:
In addition to the simple locators, CSS and XPath can selector more complex elements. Rather than saying I want all the DIV tags, I can say I want all the DIV tags whose parent is a SPAN. In CSS this would be "span>div" and in XPath this would be "//span/div".
The combinations are endless. For each tag in the XPath or CSS I can add multiple identifiers. So I could have locators for things as complex as "all DIV tags, with name containing 'foo' and class equal 'bar' whose parent is a TD but only in the TABLE with id='summary' and the class equal 'humho'"
The first thing to understand is that CSS will be noticeably faster than XPath when testing against Internet Explorer. Your tests could run as much as 10 times slower (something which runs on a day on Firefox could take a week on Internet Explorer) when using XPath.
So the first thing to remember is CSS is better than XPath. However, some things are easier to express as XPath. So occasionally you might need to use XPath.
If you have a selector like "html>body>table>tbody>tr[2]>td[3]>a" it might work but if the developer finds it does not format nicely on Chrome, they need to throw in a DIV. So the selector changes to "html>body>div>table>tbody>tr[2]>td[3]>a". Later a new version of Internet Explorer comes out and the developer finds they need to add a SPAN to make it look proper on the new Internet Explorer and still look okay on older versions. So the locator becomes "html>body>div>table>tbody>tr[2]>td[3]>span>a".
If we spend all our time maintaining the locators, it could end up that the cost of maintaining the automation is greater than running the tests manually. In which case the automation is deemed a failure.
So you have to start looking for patterns. Is there something I could use on the first version of the application which also works on the second and third version? Can I predict a locator which will work on the fourth and subsequent versions?
Often the underlying technology changes but it continues to look the same to the user. So is there something visual I can use which will not change? In this example, the text for the anchor probably never changed. So I'd use By.linkText("whatever"); locator or By.xpath("//a[text()='whatever']);.
What if I find myself changing locators because sometimes the text is " whatever", sometimes it is "whatever" and other times it is "whatever "? Then I'm going to use By.partialLinkText("whatever"); or By.xpath("//a[contains(text(), 'whatever')]");.
The danger is that there might be two links which contain the substring "whatever". I need to make sure I am selecting the correct link. So the locator might need to be more complex. It might need to be partial text and parent information. For example, if the text appears in two different tables and I want the text from table 2. Table 2 has the id='foo2' then the locator might be:
If you are using Internet Explorer 8 or higher you can press F12 to open the developer tools. If you are using Firefox you need to install Firebug then F12 will open Firebug. If you are using Chrome then CTRL-SHIFT-I will open the developer tools.
Beyond that, the only tool I use is my brain and the W3 standards.
Reading the W3 standards (or any standards documentation, ISO, ANSI, IEEE, etc.) can be difficult at first. Especially if you have been learning from books like "Web Design in 21 Days" or "Software Testing for Dummies." However, the more you read and understand standards documentation, the easier it gets to read other standards documents. If generating XPath was easy enough for a piece of software then why would they pay you to do the work? There are probably a dozen XPath locators for any given element on a page. Some will work once and need to be revised on the next release of the application. Some will work within the design pattern of the application and might never need updating. There is no way for a piece of software to spot the design pattern and know which locator will work best. This is what they pay you to do.
Excessively long XPath is brittle and will need a great deal of revising from release to release. Extremely short XPath will sometimes find the wrong element between releases. This leads to a test which fails unpredictably and can be difficult to debug. Not something you want in an automation suite. Finding the right balance is your job. The first time you select a locator it might need revising for the next release. You need to look at why you selected the first locator when selecting the revised locator. The second locator should work for the first release and the second release. When the locator fails, you need to select a new locator which would have worked on the first release and all subsequent releases, including the next release. After a while you should start to see the pattern. The pattern is usually derived from some design pattern the application is being developed with. Learn about Design Patterns, it will be extremely helpful in generating good test automation. If the developers change the tools, libraries, design patterns, etc. you should expect the locators to fail. At this point, selecting a locator which works with the next release but does not work with the previous release makes sense. Major change in development usually implies major change in test automation. It would be difficult for a tool to realize when it needs to abandon old locators.
Essentially, automation is all about finding elements (locators), performing actions on them, confirming the expected results (usually involves more locators). Two thirds of the work is about the locators. Learning XPath, CSS and DOM will make your job that much easier.
When possible, use CSS selectors as they are faster. Some things are easier to locate using XPath and XQuery (XPath functions). It is better to have a test run slow and be easy to maintain. So if CSS selectors are complex and unintuitive you might want to use XPath functions instead.
This is essentially how I decide on locators.
.
- Find an element
- Perform an action
- Confirm the expected result
Finding an element and confirming the expected result requires locators. In WebDriver a locator is a way of uniquely identifying an element on the web page, i.e. in the Document Object Model (DOM). The By class is used in WebDriver to locator elements. You have:
- By.className(className);
- By.id(id);
- By.linkText(linkText);
- By.name(name);
- By.partialLinkText(linkText);
- By.tagName(name);
- By.cssSelector(selector);
- By.xpath(xpathExpression);
The most powerful locators are CSS and XPath. All the other selectors can actually be done using CSS or XPath. For example:
| Original | CSS | XPath |
|---|---|---|
| By.className("foo"); | By.cssSelector(".foo"); | By.xpath("//*[@class='foo']"); |
| By.id("bar"); | By.cssSelector("#bar"); | By.xpath("//*[@id='bar']"); |
| By.linkText("Click Me"); | N/A | By.xpath("//a[text()='Click Me']"); |
| By.name("fee"); | By.cssSelector("[name='fee']"); | By.xpath("//*[@name='fee']"); |
| By.partialLinkText("some"); | N/A | By.xpath("//a[contains(text(),'some')]"); |
| By.tagname("div"); | By.cssSelector("div"); | By.xpath("//div"); |
In addition to the simple locators, CSS and XPath can selector more complex elements. Rather than saying I want all the DIV tags, I can say I want all the DIV tags whose parent is a SPAN. In CSS this would be "span>div" and in XPath this would be "//span/div".
The combinations are endless. For each tag in the XPath or CSS I can add multiple identifiers. So I could have locators for things as complex as "all DIV tags, with name containing 'foo' and class equal 'bar' whose parent is a TD but only in the TABLE with id='summary' and the class equal 'humho'"
The first thing to understand is that CSS will be noticeably faster than XPath when testing against Internet Explorer. Your tests could run as much as 10 times slower (something which runs on a day on Firefox could take a week on Internet Explorer) when using XPath.
So the first thing to remember is CSS is better than XPath. However, some things are easier to express as XPath. So occasionally you might need to use XPath.
If you have a selector like "html>body>table>tbody>tr[2]>td[3]>a" it might work but if the developer finds it does not format nicely on Chrome, they need to throw in a DIV. So the selector changes to "html>body>div>table>tbody>tr[2]>td[3]>a". Later a new version of Internet Explorer comes out and the developer finds they need to add a SPAN to make it look proper on the new Internet Explorer and still look okay on older versions. So the locator becomes "html>body>div>table>tbody>tr[2]>td[3]>span>a".
If we spend all our time maintaining the locators, it could end up that the cost of maintaining the automation is greater than running the tests manually. In which case the automation is deemed a failure.
So you have to start looking for patterns. Is there something I could use on the first version of the application which also works on the second and third version? Can I predict a locator which will work on the fourth and subsequent versions?
Often the underlying technology changes but it continues to look the same to the user. So is there something visual I can use which will not change? In this example, the text for the anchor probably never changed. So I'd use By.linkText("whatever"); locator or By.xpath("//a[text()='whatever']);.
What if I find myself changing locators because sometimes the text is " whatever", sometimes it is "whatever" and other times it is "whatever "? Then I'm going to use By.partialLinkText("whatever"); or By.xpath("//a[contains(text(), 'whatever')]");.
The danger is that there might be two links which contain the substring "whatever". I need to make sure I am selecting the correct link. So the locator might need to be more complex. It might need to be partial text and parent information. For example, if the text appears in two different tables and I want the text from table 2. Table 2 has the id='foo2' then the locator might be:
- "table.foo2 a"
- "//table[@id='foo2']/tbody/tr/td/a[contains(text(),'whatever')]"
The first locator assume there is only 1 anchor in the second table. This might not be true in all cases. The second locator finds the second table but it searches all rows (TR) and all columns (TD) for an anchor (A) whose text contains the substring "whatever". This can be extremely slow, especially for large tables.
Finding the balance between locators which are too long and too short can be an art. The trick is to pick something. If it requires maintenance, pick a new locator which works on the previous versions and the new version. As you continue to maintain the locators you will see a pattern. You will start to see that chunks of HTML code never change Outside these chunks change (so keep the locator short enough to stay inside the chunk that does not change). Within the chunk there might be multiple matches if you make the locator too short. So figure out, within that chunk, what makes the element you want different from all the other matches.
So how do I look at the DOM? I need to see what the DOM looks like to be able to see all the possible locators which would work.
If you are using Internet Explorer 8 or higher you can press F12 to open the developer tools. If you are using Firefox you need to install Firebug then F12 will open Firebug. If you are using Chrome then CTRL-SHIFT-I will open the developer tools.
Beyond that, the only tool I use is my brain and the W3 standards.
Reading the W3 standards (or any standards documentation, ISO, ANSI, IEEE, etc.) can be difficult at first. Especially if you have been learning from books like "Web Design in 21 Days" or "Software Testing for Dummies." However, the more you read and understand standards documentation, the easier it gets to read other standards documents. If generating XPath was easy enough for a piece of software then why would they pay you to do the work? There are probably a dozen XPath locators for any given element on a page. Some will work once and need to be revised on the next release of the application. Some will work within the design pattern of the application and might never need updating. There is no way for a piece of software to spot the design pattern and know which locator will work best. This is what they pay you to do.
Excessively long XPath is brittle and will need a great deal of revising from release to release. Extremely short XPath will sometimes find the wrong element between releases. This leads to a test which fails unpredictably and can be difficult to debug. Not something you want in an automation suite. Finding the right balance is your job. The first time you select a locator it might need revising for the next release. You need to look at why you selected the first locator when selecting the revised locator. The second locator should work for the first release and the second release. When the locator fails, you need to select a new locator which would have worked on the first release and all subsequent releases, including the next release. After a while you should start to see the pattern. The pattern is usually derived from some design pattern the application is being developed with. Learn about Design Patterns, it will be extremely helpful in generating good test automation. If the developers change the tools, libraries, design patterns, etc. you should expect the locators to fail. At this point, selecting a locator which works with the next release but does not work with the previous release makes sense. Major change in development usually implies major change in test automation. It would be difficult for a tool to realize when it needs to abandon old locators.
Essentially, automation is all about finding elements (locators), performing actions on them, confirming the expected results (usually involves more locators). Two thirds of the work is about the locators. Learning XPath, CSS and DOM will make your job that much easier.
- XPath 1.0: http://www.w3.org/TR/xpath/
- XPath 2.0: http://www.w3.org/TR/xpath20/
- XPath functions: http://www.w3.org/TR/xpath-functions/
- CSS 1.0: http://www.w3.org/TR/2008/REC-CSS1-20080411/
- CSS 2.0: http://www.w3.org/TR/CSS2/
- CSS 3.0: http://www.w3.org/TR/selectors/
- DOM: http://www.w3.org/TR/DOM-Level-2-HTML/html.html
When possible, use CSS selectors as they are faster. Some things are easier to locate using XPath and XQuery (XPath functions). It is better to have a test run slow and be easy to maintain. So if CSS selectors are complex and unintuitive you might want to use XPath functions instead.
This is essentially how I decide on locators.
.
Labels:
automation,
CSS,
DOM,
Selenium,
software testing,
web testing,
WebDriver,
xpath
Subscribe to:
Posts (Atom)