Helgevold Consulting, LLC

How To Write XPath for Selenium Tests

Author:Torgeir Helgevold
Written: 9/8/2010

All articles

In a previous article I discussed how to use XpathItUp to produce xpath expressions for Selenium tests. There have been several updates to XpathItUp since that article was written, so the purpose of this article is to give a more comprehensive overview of the fluent XpathItUp API, and how it fits into Selenium tests.

Basic Elements

A common tasks when writing Selenium tests is to verify the presence (or absence) of a particular element on a web page. The simplest case of this is to verify the text content of a tag. As an example, lets assume you want to write a web test to verify that a logged in user gets a standard welcome message upon logging into your site. The welcome message might look like the following:

<div>Welcome Joe Smith!</div>

The standard way to write a web test to verify this message is to look for a div tag with the text "Welcome Joe Smith!". The fluent XpathItUp way to generate the xpath expression for this is:

XPathFinder.Find.Tag("div").With.Text("Welcome Joe Smith!").ToXPathExpression()

Here's how to include the expression in an Assert:

Assert.IsTrue(selenium.IsElementPresent(XPathFinder.Find.Tag("div").With.Text("Welcome Joe Smith!").ToXPathExpression()));

It's important to keep in mind that XpathItUp's Text() method generates an xpath expression that will do a case sensitive exact match on the input string. In order to match on a partial string we can change the expression to the following:

XPathFinder.Find.Tag("div").Containing("Joe Smith!").ToXPathExpression()
Keep in mind, the partial string match is still case sensitive.

Attributes

The natural addition to this is expressions based on elements with attributes. Lets continue the example from above, but add a class attribute to the div.

<div class="welcomeMessage">Welcome Joe Smith!</div>

To include the new class attribute we will simply change the XpathItUp expression to

XPathFinder.Find.Tag("div").With.Text("Welcome Joe Smith!").And.Attribute("class", "welcomeMessage").ToXPathExpression()

Attribute queries may also be combined with partial text matches:

XPathFinder.Find.Tag("div").Containing("Welcome Joe").And.Attribute("class", "welcomeMessage").ToXPathExpression()

It is also supported to include partial matches on attributes. To illustrate this we will change the markup to include an id attribute:

<div class="welcomeMessage" id="parent1_parent2_welcomeMessage">Welcome Joe Smith!</div>

Locating elements by the id attribute may in some cases result in fragile tests since the id of an html element is influenced by where it appears in the hierarchy. This means that changes to DOM ancestors of the element may change the id. A way to reduce this problem is to do a partial match on the id attribute like so:

XPathFinder.Find.Tag("div").With.Text("Welcome Joe Smith!").And.Attribute("class", "welcomeMessage").And.Attribute("id").Containing("_welcomeMessage").ToXPathExpression()

Attributes can be "anded" together to include any attribute defined on a given element.

Children

In some cases it's useful to create expressions to locate elements based on how they appear in relation to other elements.

Let's change our example to include a parent on our original div element:

<div class="main"><div class="welcomeMessage" id="parent1_parent2_welcomeMessage">Welcome Joe Smith!</div></div>

To include the parent all we have to do is change the XpathItUp expression to the following:

XPathFinder.Find.Tag("div").With.Child("div").With.Text("Welcome Joe Smith!").ToXPathExpression()

or to even include the class attribute, we may do:

XPathFinder.Find.Tag("div").With.Attribute("class","main").And.Child("div").With.Text("Welcome Joe Smith!").ToXPathExpression()

Parent

There is also a way to specify a parent on an element:
XPathFinder.Find.Tag("div").With.Parent("div").ToXPathExpression()

Sibbling

There is also support for finding elements in relation to their siblings

Following Sibling

Let's expand our current example to include a "following sibling" of type div
<div class="welcomeMessage" id="parent1_parent2_welcomeMessage">Welcome Joe Smith!</div>
<div>You have 2 new messages</div>

To include the sibling in our expression simply change it to:

XPathFinder.Find.Tag("div").With.Text("Welcome Joe Smith!").And.FollowingSibling("div").With.Text("You have 2 new messages").ToXPathExpression()

Preceding Sibling

To illustrate preceding siblings let's change the markup to
<div>Some headline</div>
<div class="welcomeMessage" id="parent1_parent2_welcomeMessage">Welcome Joe Smith!</div>
<div>You have 2 new messages</div>

The preceding sibling can be included as follows:

XPathFinder.Find.Tag("div").With.Text("Welcome Joe Smith!").And.PrecedingSibling("div").With.Text("Some headline").ToXPathExpression()

Limitations

The xpath expressions generated by XpathItUp are designed to verify the presence of elements on the page, which is ideal for Selenium's "IsElementPresent". However, in the current version the expressions are not designed to always return the result set representing the actual content of the element specified in the Tag() method. This does not create a problem if you are trying to assert that something is present or absent on the page, but it may be a problem if you are relying on the actual content of the node itself. An example would be to generate an xpath to click a button somewhere on the page during test execution. This limitation is due to the orientation of the xpath queries produced, and how xpath queries are executed. Take the following example:

XPathFinder.Find.Tag("div").With.Child("span").With.FollowingSibling("span")

This expression will result in the underlying xpath expression: //div/span/following-sibling::span which will work well if you pass it to selenium's IsElementPresent() since it checks for the Boolean condition of whether or not there is a result set for the query. You can basically view the expression as a where clause with multiple "and" conditions. If the query comes back with a result, we have effectively verified that the hierarchy we are checking for is present on the page. The limitation however will come up in cases where we need to work with the actual content of the result set returned from the query. Because of how xpath queries are executed, the single node we get back is the following sibling of type "span", and not the div which might be the expected result. This is because xpath expressions return the element to the right of the last "/". This may as mentioned above result in some unexpected results if the expression is passed to Selenium's Click() method which works directly with the content of the returned element. Basically, with this expression, we would be telling Selenium to click the sibling and not the div. While this is a limitation when it comes to click interactions on the page, it's worth pointing out that the limitation applies only to expressions that end with the Child(), FollowingSibling() and PrecedingSibling() methods. This is because these methods will end up appending to the right end of the query. I will address this limitation in a subsequent release of XpathItUp.

If this article has made you curious about using XpathItUp you may download it from the following location:

comments powered by Disqus