Make Xpath Concise via Jump-Locating

    Locating element by Xpath is slow, fragile and has bad readability thus bug-prone, and on top of that:

    • Xpath engines are different in each browser, hence make them inconsistent
    • IE does not have a native xpath engine, therefore selenium injects its own xpath engine for compatibility of its API. Hence we lose the advantage of using native browser features that WebDriver inherently promotes.

    However there are some situations where, you need to use xpath, for example, searching for a parent element or searching element by its text. While we avoid Xpath whenever we can, any way to improve if we have to use it? Yes, fortunately:

    • Always use Relative Xpath. Never use Absolute Xpath
      Absolute Xpath uses complete path from root to the desired element, while Relative Xpath starts by referencing the element you want and go from there. Relative Xpath is thus much less likely to change when developers modify the page.
    • Choose the nearest stable ancestor as anchor element, then jump along the major descendants to your desired element.
      Xpath syntax for:

      Direct descendents:/
      example: //div/div/ul/li/span
      Any descendents://
      example: //div//div//ul//li//span

      Use ‘//” in the path, instead of ‘/’, to skip nodes that are not necessary for the path, especially those fragile nodes.

    • Don’t use the Xpath given by your browser inspector.
      Firefox’s firebug add-on and Chrome’s inspect tool make it super-easy for us to get any element’s Xpath. But Xpath provided by the tool is lengthy and most time absolute Xpath. Don’t use it. Use it as a base for optimizing per instruction below

    Choose Anchor Element for Relative Xpath
    What makes a good anchor element as the starting point for Relative Xpath?

    • First, it has to be an ancestor relative closer to the desired element
    • It has to be stable, ie. not likely to subject to HTML change in the future;
    • Preferably it has an ID, making the driver to find it faster

    Then Jump Along MileStone Descendants to the Desired Element.

    • Choose only a few milestone descendants along to the way to desired element that are less likely to change in the future. The idea is to make Xpath more readable, brief and concise. Skip unnecessary nodes using ‘//’
    • When identifying descendants, add flexibility by using key words like “starts-with” or “contains“.
    • Start-with function matches the starting text of the attribute, majorly for elements whose attribute changes on refresh or on any operation. An example:

      //find elements from current node which are labels and whose ID starts with ‘message’
      Xpath=//label[starts-with(@id,’message’)]

      Contains() is used to find the element with partial text, mostly when the value of any attribute changes dynamically, an example:

      //find elements whose name attribute contains ‘btn’
      Xpath=.//*[contains(@name,’btn’)]
    • Make the path as short as possible. Filter out unnecessary on-the-way descendants.

    Use following html section as an example:

    <body>
    <div class=”genres-wrapper wrapper”>
    <ul class=”genres”>
    <li class=”genre”>
    <div class=”genre-btn” >
    <div class=”genre-img” ></div>
    <button class=”btn-circle check”>
    <i class=”icon-check genre-btn-check”></i>
    </button>
    <div class=”genre-title”>Classic Rock</div>
    </div>
    </li>
    <li class=”genre”>
    <div class=”genre-btn” >
    <div class=”genre-img”></div>
    <button class=”btn-circle check”>
    <i class=”icon-check genre-btn-check”></i>
    </button>
    <div class=”genre-title”>Pop</div>
    </div>
    </li>
    </ul>
    </div>
    </body>

    Suppose that we want to locate ‘Pop’ element by xpath, of course I mean relative xpath, then where shall we start?

    The absolute xpath would be: /html/body/div/ul/li[2]>div>div[@class=’genre-title’], But of course, NO Absolute Xpath. Let’s work out a concise relative xpath for it!

    First, choose a proper start element: All the genre information is put in a ul with class ‘genres’ and this is not likely to change even if later on developers will add new genres to the list. So this node ul[@class=’genres’] is relatively stable and it is a good candidate to use is as starting node for our relative xpath.

    So what elements on the way to our target element can be skipped? li[2] is absolutely necessary to indicate where our target element resides, But the div after it can be skipped. Thus we get the following:

    //ul[@class=’genres’]/li[2]//div[@class=’genre-title’]

    And there we are: A much more concise and yet more resilient way to locate this element!

Leave a Reply

Your email address will not be published. Required fields are marked *