- Open a web page.
- Locate an html element.
- Interact with it in some way. i.e. click it, type into it, select an option
- Locate another html element.
- Interact with it.
- By name.
- By id.
- Using XPath.
Step 1 – Navigate to the problematic web pageThe first step is to manually navigate to the web page which your test is having a problem with. I’ve recently been working on a test that purchases a product by selecting a customer, then a product, then entering address details, then finally credit card details. I used the Selenium IDE to record the test and then saved it as Java code for playback. However, when running, it failed at the stage of entering the credit card details, specifically the card number. Have a look at the following screenshot and see if you can tell why: Did you spot it? There are two “card number” fields on the page. The recorded Selenium test was picking up the one for the gift card. You can see that we need the one in the credit card section, so how do we go about changing the selector to pick this out?
Step 2 – Write and test your XPath expressionThere are a number of ways to write and test an XPath expression:
- Using the Selenium IDE itself – it has a “test” option.
- Using Firebug and a separate XPath tester.
- Using Firebug and the FirePath tester, which is a separate add-on but integrates with Firebug.
//inputSure enough, FirePath highlights all of the inputs on the page: I know that I need the card number input within the “Credit Card” area of the page, but how do I know what XPath expression to write to pick this out? Well, you can use Firebug to inspect the DOM to understand the structure: Looking at this structure, you can see that conceptually I’m looking for the input with the id of cardNumber that is within the div that has an h3 heading of “Credit Card”. To build up an expression for this, you can start by just trying to pick out that div. For example:
//div[h3/text()='Credit Card']This correctly picks out the div, but how do you navigate down to the input? Well, in XPath you use axes to navigate up and down the DOM. The input is within the div, so we’ll need to use an axis like “child” or “descendant”. You could test that with:
//div[h3/text()='Credit Card']/descendant::*Sure enough, that picks out all of the nodes beneath the div. I know that I want an input with an id of “cardNumber”, so I change that to:
//div[h3/text()='Credit Card']/descendant::input[@id='cardNumber']This works, but actually it is overspecified. What if the heading was changed to an h2 or a legend? It is more flexible for the expression just to specify that the div must have some kind of child node with the text “Credit Card”, rather than specifically an h3 heading. Hence the final XPath expression is:
Step 3 – put the XPath expression back into your test and rerunOnce you have worked out the XPath expression, you can put it back into your test and rerun it. I’m using the Selenium 2 API for my test, so the code becomes:
webDriver.findElement( By.xpath( "//div[*/text()='Credit Card']/descendant::input[@id='cardNumber']" ) ).sendKeys("1234123412341234");If you’re using Selenium 1, your syntax will be more like:
selenium.type( "//div[*/text()='Credit Card']/descendant::input[@id='cardNumber']", "1234123412341234" );
Hopefully you’ll find that by using the above technique with Firebug and FirePath, you can build up and test even complex XPath expressions easily. If you want to read up on XPath, a good place is the w3schools tutorial: http://www.w3schools.com/xpath/default.asp Firebug can be downloaded from: http://getfirebug.com/ and FirePath from: https://addons.mozilla.org/en-us/firefox/addon/firepath/ If you want to automate your Selenium tests, you might also be interested in my blog post Automating Selenium testing with TestNG, Ant and CruiseControl