So I had someone tell me they needed a more fundamental tutorial on xpath. So lets start off with the basics:
What is Xpath? It is a short way of referencing an element on a web page.
What is it for? In selenium it is used for identifying a html element that does not have an easy, unique identifier such as id, name, text.
How does it work? Well I don't really know. But essentially it searches the web page, top to bottom, looking for elements that match the criteria.
Let's talk about html. Here is an example element: <a href=http://www.google.com>google link</a>. This will show up on the page as a text link displaying the words "google link" and it will take you to www.google.com. For each element there are three main parts: the type, the attributes, and the text.
Our element is of type a. It has an attribute called href equal to http://www.google.com. and it has text equal to "google link". We can use all three of these things to search for our elements.
The next concept to understand is the idea of nodes, and the familial relationship of html elements. Look at this example code:
<div title="Section1">
<td name="Search">
<tr class="Yahoo">Yahoo Search</tr>
<tr class="Google">Google Search</tr>
</td>
</div>
Notice the </div> at the bottom? That means the td and tr elements are contained within the div. These other elements are considered descendants of the div. The td is a child, and the tr is a grandchild (and so on and so forth). The two tr elements are considered siblings. This is vital, as xpath uses these relationships to find your element
So suppose I wanted to find the google item. Any of the following expressions will work:
//tr[@class='Google']
/div/td/tr[2]
//div[@title="Section1"]//tr[text()="Google Search"]
So lets analyze the expressions. We start at the top element (also known as a node). The // means to search all descendants, / means to just look at the current element's children. So //div means look through all descendants for a div element. The brackets [] specify something about that element. So we can look for an attribute with the @ symbol, or look for text with the text() function. We can chain as many of these together as we can.
Here is a quick reference:
// search all descendant elements
/ search all child elements
[] The predicate (specifies something about the element you are looking for)
@ Specifies an element attribute. (For example, @title)
text() Gets the text of the element.
. specifies the current node (useful when you want to look for an element's children in the predicate)
.. specifies the parent node
contains() Use this in the predicate if you can't do a full string match on an attribute or text() value.
Friday, January 7, 2011
waiting for a popup to close
I was having a problem yesterday where the popup I was working on didn't close by the time the next one was supposed to be open. I looked through the selenium api and realized there was no good waitForPopUpToClose command, so I created a new function. All it does is wait until the number of windows is 1. This will obviously only work if you have 1 window open (besides the popup).
public void waitForPopupToClose()
{
string[] titles;
for (int second = 0; second<timeoutSeconds; second++)
{
try
{
titles = selenium.GetAllWindowTitles();
if (titles.Length > 1)
sleep(1000);
else
{
break;
}
}
catch (Exception)
{
}
Thread.Sleep(1000);
}
}
public void waitForPopupToClose()
{
string[] titles;
for (int second = 0; second<timeoutSeconds; second++)
{
try
{
titles = selenium.GetAllWindowTitles();
if (titles.Length > 1)
sleep(1000);
else
{
break;
}
}
catch (Exception)
{
}
Thread.Sleep(1000);
}
}
Thursday, January 6, 2011
Xpath In All It's Glory
I thought it would be a good idea to write up a little bit about Xpath, why it rocks, and why you SHOULD use it. I know many "experts" in selenium tell you that Xpath is bad, it's slow, and it needs to be avoided at all costs. I disagree completely. I will, of course, try to reference an item in the most clean and concise method possible. So if an item has a unique id or name, you should use that and avoid an xpath expression. However, there are so many times when the element you're trying to locate does not have a unique identifier, and you need to find it.
The problem with Xpath is the way most xpath generating programs work. They generate a string of absolute locators a million miles long. So something like this: /body/td/tr[2]/div[3]/a. This is NOT how you want to use Xpath. This style of xpath expression should be your very LAST resort on locating an element. But what is the alternative? Start with the parent.
First off, I strongly recommend the use of // instead of /. It may be slightly slower locating an element, but it allows you to construct a much more transparent, and reliable xpath expression.
So something like this would be a more appropriate xpath from the above example: //div[@class='object1']/a This looks through the entire page for a div with attribute "class" equal to "object1". It then looks for a child <a> element. This is fine as long as there is only one div with class=object1. However, class is not a unique identifier, and it's quite possible there is more than one item on the page that matches our expression. What is the solution? Add another parent.
//td[@title='Row1']//div[@class='object1']/a. Neither title nor class are unique, but hopefully the combination of both is. However, maybe not.
If you can't find a parent or grandparent to start with that is unique, you can also try a sibling node. Suppose we have a table where nothing has attributes. We want to type into the text field in an adjacent row to a link. We can find the link, because the link text is unique. But we can't find the text field without resorting to some caveman xpath expression. The easiest way i've found to do this is to start with the parent node, and in the predicate (the []) look for a child node, then select another node. For example:
This expression select a row, looks for a descendant link with text "User1", then selects any descendant input. You have to use the period inside to select the current node.
//tr[.//a[text()="User1"]]//input
The problem with Xpath is the way most xpath generating programs work. They generate a string of absolute locators a million miles long. So something like this: /body/td/tr[2]/div[3]/a. This is NOT how you want to use Xpath. This style of xpath expression should be your very LAST resort on locating an element. But what is the alternative? Start with the parent.
First off, I strongly recommend the use of // instead of /. It may be slightly slower locating an element, but it allows you to construct a much more transparent, and reliable xpath expression.
So something like this would be a more appropriate xpath from the above example: //div[@class='object1']/a This looks through the entire page for a div with attribute "class" equal to "object1". It then looks for a child <a> element. This is fine as long as there is only one div with class=object1. However, class is not a unique identifier, and it's quite possible there is more than one item on the page that matches our expression. What is the solution? Add another parent.
//td[@title='Row1']//div[@class='object1']/a. Neither title nor class are unique, but hopefully the combination of both is. However, maybe not.
If you can't find a parent or grandparent to start with that is unique, you can also try a sibling node. Suppose we have a table where nothing has attributes. We want to type into the text field in an adjacent row to a link. We can find the link, because the link text is unique. But we can't find the text field without resorting to some caveman xpath expression. The easiest way i've found to do this is to start with the parent node, and in the predicate (the []) look for a child node, then select another node. For example:
This expression select a row, looks for a descendant link with text "User1", then selects any descendant input. You have to use the period inside to select the current node.
//tr[.//a[text()="User1"]]//input
Selenium and Selenium2 Visual Studio Templates
Here are some visual studio templates I created for selenium and selenium2. All you should have to do is fix the references once you import them into visual studio 2010.
They feature:
Base class with Selenium1 api commands.
.config file usable by nUnit
PageObject design
Multi-project format
Automatically launches and closes selenium server.
Relative pathing
Sample Google Test
Selenium 1 Template
Selenium 2 Template
They feature:
Base class with Selenium1 api commands.
.config file usable by nUnit
PageObject design
Multi-project format
Automatically launches and closes selenium server.
Relative pathing
Sample Google Test
Selenium 1 Template
Selenium 2 Template
Subscribe to:
Posts (Atom)