Monday, December 24, 2012

XPath to the Limit

12-11-2012

Being a newbie to Selenium as well as the programming world, I followed the Selenium tutorial to the letter.  I am pretty sure many can attest to some things were far easier to figure out than other things and somethings proved to be quite involved and challenging to learn in a short time frame.  For me, XPath was the challenge.  In this article, I will share some tips that can help you leverage the query features of XPath for advanced object identification.

One additional note, the examples given in this article is based on the java programming language.  However, the techniques are easily transferable to the preferred programming language you are using for your Selenium automation, i.e. ruby, C#, python, etc.

What’s an XPath

In short, XPath stands for the XML Path Language which is a query language for selecting nodes from an XML document.  See XPath Overview for more information.  In Selenium’s tutorial, they identify XPath as one option for identifying objects on a web page.

Like most tutorials, they only provided the very basics for using XPath as an object recognition technique.  This is ok!  It is nice they guide you to a beginning than none at all.  It is up to you, the developer, to figure out and learn how to maximize the techniques for your own unique benefits.  When we review their documentation, they will provide an example such as “List<WebElement> inputs = driver.findElements(By.xpath("//input"));”. It looks simple doesn’t it?  For people new to programming and Selenium, it might not be that straight forward.

If we look at the given example, “//input” is the only part of that example that is the XPath.  What does it tells us?  This identifier simply refers to the first control of type input in the XML document from the current node. Now, what if we don’t want the first input type?  Aha, so let’s move on and find out how we can identify other control types using XPath on our web page.

Firebug

Firebug is a great tool for interrogating the XML documents of your web application.  To learn more about Firebug, download the application here at Firebug.  This tool is a great way to view the XML document representation of your web page and to see the different options for identifying an object.  When you need to get the XPath of an object, it has a really nice feature where you can simply highlight the object in Firebug, RClick and copy the XPath associated to the object.  Let’s try a simple example.

1.  After you have installed Firebug, which is installed as a plugin to your Firefox browser, click the insect in the upper right hand corner.


2.  The screen will split and Firebug will be located in the lower pane.


3.  Browse to Google.
4.  Click the toolbar icon to inspect the page objects.

5.  Highlight the search input text field.
6.  The highlighted text box highlights the corresponding XML node in Firebug


7.  RClick the XML node and select from the context menu “Copy XPath..
8.  Paste the XPath ‘//*[@id="gs_htif0"]’ directly into your Selenium into your code.

As you can see, Firebug can be quite handy to identify objects by XPath or by other identifier types.

Complex XPaths

So now what happens when the literal XPath is not sufficient?  What if:
  • There are duplicate instances of the same object with the same identifiers?
  • Objects are dynamically created as a result of an action?
  • Worse, objects are both dynamically created and nested into other objects or tables?
  • A table contains no unique characteristics including content?
  • An object’s original identifiers change during runtime?
  • An object’s literal XPath identification changes from browser to browser?
And many other odd anomalies that make using a static XPath impossible to rely on.  When this happens, we can use variables to help us count and/or loop through a set of objects and we can take advantage of the query features available to XPath to help us develop more clever and reliable object identifiers.

Variabilizing the XPath

Iterating through sequential objects

Lifting this example from the Google home page, let’s use this sample XPath identifier that ends in a numeric value.  Given this example, you wish to iterate over the objects until you find one that meets your qualifying condition.  We can do this by replacing the hard coded integer with a variable.

Before
//*[@id="pocs0"]

After
integer i = 0;
//*[@id=”pics” + i]

This technique will allow you to use a looping or selection statement to select and test an object prior to selecting it as the object identifier.

Reusable Methods

We can also use variables to help us create reusable methods in the TestFramework.  I can share an actual common method we used that is designed to click a button by name.  Here we passed the name of the button (the bolded text) we desired to click and used that name within the XPath call.

public String ClickButtonByName(String objectName) {
String value = null;
WebElement element = null;
try {
element = currentWebDriver.findElement(By.xpath("//*[@name='" + objectName + "']"));
value = element.getValue();
element.click();
Thread.sleep(5000);
} catch (Throwable t) {
logger.debug("Did not find element:  " + objectName, t);
}
return value;
}

XPath Query

This is a powerful tool to use when you require greater than the literal XPath to locate a desired object.  You can actually find a great tutorial on the use of XPath query at XPath Query Syntax.  Here you can learn the syntax that can help you conquer hard to locate objects or dynamically created objects during the application’s runtime.  Let’s review the following example,
WebElement element = currentWebDriver.findElement(By.xpath("//td[text()='" + objectName + "']/../following-sibling::td/input[@type='checkbox']"));
The simple answer to the riddle above is given the row with text name equal objectName, get all of its root node siblings and identify the sibling whose input type is a checkbox.  This technique allowed us to locate a checkbox that was dynamically created during the application’s runtime.

Conclusion

Have fun experimenting.  Our test automation goal should be to make our TestFrameworks as reusable as possible.  If XPaths can help you achieve this goal you will find the payoff rewarding as you continue your TestAutomation endeavors.

Happy Automation!

No comments:

Post a Comment