2

I have recently started with Selenium, and I've decided to use only Selenium 2 / WebDriver. In my test code, I often arrive at a WebElement without knowing where it is in the page.

I'd like to have code that, given an WebElement, constructs an XPath expression to uniquely select it.

FireFox recorder plugins for Selenium do this; what I want is code to do it in Selenium 2.

I can write such code myself by using WebElement's findElement to walk up the tree and findElements to find the child we came from, but it's nontrivial to come up with something fast (repeatedly calling By.xpath seems bad) and complete (e.g. due to namespaces).

(A related question suggests using CSS selectors instead - that's fine with me.)

Has anyone done this for me? What is the best approach?

Community
  • 1
  • 1
reinierpost
  • 8,425
  • 1
  • 38
  • 70

3 Answers3

2

It's pretty straight forward: and btw yes css is the way to go; xpath should be only used as a measure of last resort. http://sauceio.com/index.php/2010/01/selenium-totw-css-selectors-in-selenium-demystified/ explains css locators in much more depth than I can in the space provided here.

Best approach: If you're using firefox download firebug, that will let you look at your html. Pres cmd+Shift+c and it'll open for you with an element highlighter. Find your html element, maybe it'll look something like this

<input type="submit" tabindex="110" value="Post Your Answer" id="submit-button">

Then you can find your element pretty simply

WebElement element = driver.findElement(By.cssSelector("input[id='submit-button']"))

Notice that we put the tagname first "input" followed by some kind of unique identifier in the brackets "input[id='submit-button']." For the most part this will cover 75% of all css locators you use. The other 25% require a little bit more tricky stuff covered in the link I placed at the top of the page.

You may ask "What kind of unique identifiers can I use besides id" well that is covered here: http://release.seleniumhq.org/selenium-remote-control/0.9.2/doc/dotnet/Selenium.html

Good luck starting out

EDIT

Well good luck finding your elements in the first place...If you need you can search elements by partial locator text like input[id*='submit']. Using that is helpful for dynamically generated elements, when you use the partial text as the part of the locator that doesn't vary from element to element.

You mentioned walking up the html tree perhaps I didn't see that when I first read the question. I think you hit the on issue at hand. Walking up the html tree isn't suggested as it makes your tests more fragile to html changes. It will also make your code unreadable in the long run as well. In general if your id's are missing or unpredictable I would suggest talking to proj. management about getting the developers to make code that can actually be automated (eg: getting identifiers implemented on critical elements). This will actually save both you and the developers alot of effort in the long run, and it will also increase the speed and reliability of your tests.

Greg
  • 5,422
  • 1
  • 27
  • 32
  • Thanks! Unfortunately, this doesn't really help me, because most of the ids on my elements are missing or unpredictable. – reinierpost Aug 20 '12 at 08:45
2

Maybe this will help: I am testing a website where the id's are dynamically generated, so they change all the time. In order to get their xpath and work with it I use this function:

    /**
 * Gets the absolute xPath for elements with dynamic ids.
 * 
 * @param driver - the current WebDriver instance
 * @param element - the element for which the xPath will be found
 * @return the absolute xPath for this element
 */
public static String getElementXPath(WebDriver driver, WebElement element) {
    return (String)((JavascriptExecutor)driver).executeScript(
            "gPt=function(c)" +
            "{" +
            "if(c.id!=='')" +
            "{return c.tagName + '[@id=\"'+c.id+'\"]'}" +
            "if(c===document.body)" +
            "{return c.tagName}" +
            "var a=0;" +
            "var e=c.parentNode.childNodes;" +
            "for(var b=0;b<e.length;b++)" +
            "{var d=e[b];" +
            "if(d===c)" +
            "{return gPt(c.parentNode)+'/'+c.tagName+'['+(a+1)+']'}" +
            "if(d.nodeType===1&&d.tagName===c.tagName)" +
            "{a++}" +
            "}" +
            "};" +
            "return gPt(arguments[0]);", element);
}
Maria Lingo
  • 131
  • 1
  • 3
  • Sure, that will work if you extend it to also find elements deep down in the page, but once you use JavaScript I think it is more efficient to go up from the element towards the document root like I described. How portable is this solution? – reinierpost Aug 20 '12 at 08:42
  • This doesn't go all the way up, just until it finds an element with id. As for portability, I have tested only on Firefox, if that's what you mean. – Maria Lingo Aug 21 '12 at 09:10
0

We can find the Submit Button using ID:

1.driver.findElement(By.id("submit-button"))

ID > NAME > CSS > XPATH

user3487861
  • 340
  • 2
  • 2
  • Thanks! Unfortunately, this doesn't really help me, because most of the ids on my elements were missing or unpredictable. – reinierpost Apr 07 '14 at 10:05