mac
mac

Reputation: 930

How to eliminate xpaths in Webdriver code?

I working on automating a website and I'm trying to find a way to reduce the use of xpaths in my code. My code looks something like this

    driver.findElement(By.xpath("//html/body/center/div/div/center/table/tbody/tr/td/form/table/tbody/tr[3]/td/input")).click();
    driver.findElement(By.xpath("//html/body/div/table/tbody/tr/td/table/tbody/tr/td/table/tbody/tr/td")).getText();
    driver.findElement(By.xpath("//html/body/div/table/tbody/tr/td/div[2]/div/div/div[4]/div/div/div")).click();
    driver.findElement(By.xpath("//html/body/div[3]/div/div/div/div[2]/div/span/ul[2]/li[6]/a")).click();
    /*driver.findElement(By.xpath("//html/body/div/div/div/div[3]/div/div[2]/div[2]/table/tbody/tr/td[3]/table/tbody/tr/td[2]/em/button")).click();
    WebElement editUserForm = driver.findElement(By.cssSelector("iframe[src*='editUserForm']"));

Is there any way I can reduce these xpaths so that my codes don't look shabby ? One of the member here suggested me "Please don't use absolute xpath". What does it mean ? Please help. Also let me know if there any link that will help me on this.

Is it possible to create a file that will make a string to xpath pointer and then we can just use the string in the code ?

Upvotes: 0

Views: 5271

Answers (2)

user3487861
user3487861

Reputation: 350

Here again Y XPath Y Not to go with CSS

css=.uploadDetails > .docSpan -- .doc

css=.uploadDetails > .excelSpan -- .xls

css=.uploadDetails > .txtSpan -- .txt

Simple

Upvotes: 0

Arran
Arran

Reputation: 25076

You should invest in Page Objects, it's a very useful pattern:

https://code.google.com/p/selenium/wiki/PageObjects

It will help seperate the test from the implementation. By that I mean, the test should be left detailing what steps are taken, the implementation (the 'page object') should detail how those steps are taken.

As for the XPath's themselves, yes, they are pretty bad & unreliable. I would assume you've had them automatically generated for you by some tool, maybe the IDE or Firebug. Please throw these away now, don't ever use it again and read some XPath tutorials - even the XPath spec is a great learning place.

Firstly, drop the //html/body bit, it's unneeded, and makes it look worse. It will work fine without it.

Secondly, yes, having absolute XPaths (i.e, those that are based upon position) are bad. In your first XPath:

//html/body/center/div/div/center/table/tbody/tr/td/form/table/tbody/tr[3]/td/input

See that final tr[3]? What if I was to stick another row (tr) before that one? It means now your XPath should be:

//html/body/center/div/div/center/table/tbody/tr/td/form/table/tbody/tr[4]/td/input

So it means the test will break. However, the test will break not because the functionality it's testing is broken, it's broken because the test implementation is broken. Your tests should not be that flaky. If a developer wants to stick a tr above that one you want, fine, unless it breaks the actual thing you are testing, then your test should still pass.

You should think about it differently. Think about other ways of getting to that element. Let's say I have this:

<div id="divUpload">
<span class="uploadDetails">We only support the following files:
    <span class="docSpan">.doc</span>
    <span class="txtSpan">.txt</span>
</span>
<button type="submit">Upload the file<submit>
<div>

I want to get the span with docSpan as it's class name.

I could either:

//div/span/span[1]

Which would work, but what if it's changed to:

<div id="divUpload">
<span class="uploadDetails">We only support the following files:
    <span class="excelSpan">.xls</span>
    <span class="docSpan">.doc</span>
    <span class="txtSpan">.txt</span>
</span>
<button type="submit">Upload the file<submit>
<div>

It means the [1] bit is no longer valid.

So I could:

//div/span[@class='uploadDetails']/span[text()='.doc']

Which means, it's only going to fail if the span I want is taken out of being a child of the span with a class uploadDetails or the uploadDetails class is taken off the span altogether.

Or I could:

//div/descendant::span[text()='.doc']

This means it's only going to fail if the span is moved altogether, and is no longer a child of that 'parent' div.

Bottom line is don't rely on positions. Think about what's near that element. Is there something with a class? Is there something with text inside it? Is there something with an ID near it?

Upvotes: 11

Related Questions