Tuesday, June 30, 2015

Finding an Element With Webdriver: Part III

Today we will tackle the most difficult ways to find an element: by css and xpath.  These are the most difficult because it's so easy to miss a slash or a period in the description.  They are also the most difficult because they are so brittle- every time a developer makes a change to the webpage structure, the navigation path will change and your findElement command will no longer work.

However, finding an Element by css or xpath is often very needed, because developers often don't take the time to give their elements easy and unique ids or names.  I would recommend using these strategies as a last resort to find the element you need, and I would try css first before resorting to xpath.

First, let's find an element with css.  Take a look at the table that was described in the last blog post:

<table border="2">
<tr>
<td>Row 1, Column 1</td>
<td>Row 1, Column 2</td>
</tr>
<tr>
<td>Row 2, Column 1</td>
<td>Row 2, Column 2</td>
</tr>
</table>

Let's say you want to find the td element that is in the first column of the second row.  The html doesn't give you much to work with.  You can't just do a findElement by tagName, because there are four different elements with the td tag name.  CSS selectors give you a way to navigate to the precise element you want.

In this example, we first want to select the correct row.  We can do this using the nth-of-type descriptor.  Nth-of-type finds a child element where there are more than one child elements in a parent.  In this case, the parent element is the table, and the child element is the row (the tr).  Because the second row is the second tr type in the table, we can describe this second row as tr:nth-of-type(2).
Next, we'll want to find the first td element in that row.  Because it is the first td element in the row, we can just refer to it as td.  (If we needed the second element, we could describe it as td:nth-of-type(2).)

Now we'll put these two descriptions together:
findElement(By.css("tr:nth-of-type(2) td"));

This instruction tells Webdriver to first find a tr element that is the second tr child of its parent, and then within that element, to look for the first td element.

Another way to use css is to find classes.  Let's say for example that the row we are looking for has this in it: class="rowwewant".  We can find classes with css by using a period:
findElement(By.css(".rowwewant td");

This instruction tells Webdriver to first find the element with the class name "rowwewant" and then within that element, to look for the first td element.

Now let's find the same element with xpath.  In order to find the element, we will need to traverse through the DOM to get there.  First we will find the table, then find the second row, then find the first td element:

findElement(By.xpath("//table/tr[2]/td[1]");

First the table will be located, then the second row within the table, then the first td element within the row.

The two slashes at the beginning of the xpath indicate that is a relative xpath- the element will be found near the test's current location.  If this is not effective, an xpath can be created from the root element (see the previous blog for the html for the entire webpage):

findElement(By.xpath("/html/table/tr[2]/td[1]");

There are many more ways that findElement by css or xpath can be used, but hopefully this will provide a jumping-off point for finding elements that can't be found by easier means.