2
votes

I am looking to avoid using xpaths that are 'xpath position'. Reason being, the xpath can change and fail an automation test if a new object is on the page and shifts the expected xpath position.

But on some web pages, this is the only xpath I can find. For example, I am looking to click a tab called 'FooBar'.

If I use the Selenium IDE FireFox plugin, I get:

//td[12]/a/font

If I use the FirePath Firefox plugin, I get:

html/body/form/table[2]/tbody/tr/td[12]/font

If a new tab called "Hello, World" is added to the web page (before FooBar tab) then FooBar tab will change and have an xpath position of

//td[13]/a/font

What would you suggest to do?

TY!

3

3 Answers

3
votes

Instead of using absolute xpath you could use relateive xpath which is short and more reliable.

Say

<td id="FooBar" name="FooBar">FooBar</td>

By.id("FooBar");
By.name("FooBar");

By.xpath("//td[text()='FooBar']")   //exact match
By.xpath("//td[@id='FooBar']")       //with any attribute value
By.xpath("//td[contains(text(),'oBar')]")   //partial match with contains function
By.xpath("//td[starts-with(text(),'FooB')]")  //partial match with startswith function

This blog post may be useful for you.

1
votes

Relative xpath is good idea. relative css is even better(faster) If possible suggest/request id for element. Check also chrome -> check element -> copy css/xpath

0
votes

Using //td is not a good idea because it will return all your td nodes. Any predicate such as //td[25] will be a very fragile selection because any td added to any previous table will change its result. Using plugins to generate XPath is great to find quickly what you want, but its always best to use it just as a starting point, and then analyze the structure of the file to write a locator that will be harder to break when changes occur.

The best locators are anchored to invariant values or attributes. Plugins usually won't suggest id or attribute anchors. They usually use absolute positional expressions. If can rewrite your locator path in terms of invariant structures in the file, you can then select the elements or text that you want relative to it.

For example, suppose you have

<body> ...
  ... lots of code....
  <h1>header that has a special word</h1>
  ... other tags and text but not `h1` ...
  <table id="some-id">
     ...
     <td>some-invariant-text</td>
     <td>other text</td>
     <td>the field that you want</td>
  ...

The table has an ID. That's the best anchor. Now you can select the table as

//table[@id='some-id']

But many times you don't have the id, or even some other invariant attribute. You can still try to discover a pattern. For example: suppose that the last <h1> before the table you want contains a word you can match, you could still find the table using:

//table[preceding::h1[1][contains(.,'word')]]

Once you have the table, you can use relative axes to find the other nodes. Let's assume you want an td but there are no attributes on any tbody, tr, etc. You can still look for some invariant text. Tables usually have headers, or some fixed text which you can match. In the example above, if you find a td that is 2 fields before the one that you want, you could use:

//table[preceding::h1[1][contains(.,'word')]]/td[preceding-sibling::td[2][.='some-invariant-text']]

This is a simple example. If you apply some of these suggestions to the file you are working on, you can improve your XPath expression and make your selection code more robust.