Learning XPath by examples in Selenium, tips in automated test cases

Johnny Lai
4 min readSep 30, 2020

Part 1. XPath examples

Relative Path : //ul[@class='home-list']//a[text()='Querying']

  1. Locate element by id
Locate any div with id 'account-form'//div[@id='account-form']

2. Locate element by index

If the page have 3 input field and we want to locate the 1st one//input[1]2nd one, vice versa…//input[2]last one//input[last()]

3. Parentheses ( )

Some of the page maybe contains more than one forms, it may contains several forms and each form have different input elements. As a result, simply placing index after will return unexpected result//input[1]In fact, the above code will locate all input elements which is the first element within their parent element<div>
<form>
<input id="a" type="text"></input>
<input id="b" type="text"></input>
</form>
</div>
<div>
<form>
<input id="c" type="text"></input>
<input id="d" type="text"></input>
</form>
</div>
//input[1] - will return input element id:a and input element id:cResolution (//input)[1] - will return the first input element within the page

4. Locate element by text (exact match, case sensitive), which text means the content between tag. For example”<div>abc</div>”, “abc” is the text we’re looking for

Locate any button with text equal to 'Register'//button[text()='Register']

5. Locate element by text (partial match, case sensitive)

Locate any button with contains text 'Account'//button[contains(text(),'Account')]Locate any button with CSS class which starts with 'form-style'//button[starts-with(@class, 'form-style')]Use case
i. Some class name are auto generated, with random numbers suffix which is different from time to time, with contains syntax, we can ignore the numbers

Two buttons returned, we can append the above xpath with index to narrow the result

First button with 'Account' text//button[contains(text(),'Account')][1]Second button with 'Account' text//button[contains(text(),'Account')][2]

6. Locate element by type

Text input//input[@type='text']Password input//input[@type='password']Checkbox//input[@type='checkbox']Radio//input[@type='radio']

Of coz, we can append the above xpath with index to narrow the result

1st Text input//input[@type='text'][1]2nd Password input//input[@type='password'][2]3rd Checkbox//input[@type='checkbox'][3]4th Radio button//input[@type='radio'][4]

7. Locate element by CSS class

button with class 'registerbtn'//button[@class='registerbtn']

8. Locate element by multiple condition

div contains style class with text 'abc' and 'def', but do not have any style class with text 'ghi'This is 'contains' not equal, partial match onlyUse casei. Element contains multi css class//div[contains(@class, 'abc') and contains (@class, 'def') and not(contains (@class, 'ghi'))]

9. Single slash ‘/’ vs Double slash ‘//’

Child only

Locate input elements which is immediate under div//div[@class='streams-tabs-section']/input

All Descendant

Locate input elements which is under div//div[@class='streams-tabs-section']//div

10. Wildcard

Any element contains text 'keyword'//*[contains(text(),'keyword')]

11. Parent / child / ancestor / descendant

Any div which contains p //div[p]Any p element which contained by a div which contains p//div[h4]//p

Consider the following code snippet from HTML form

<div class="input-container">
<label><b>Name</b></label>
<input type="text" required>
</div>
<div class="input-container">
<label><b>Email</b></label>
<input type="text" required>
</div>
Besides using index, we can make use of the label text to locate the input element with the help of 'ancestor' syntaxi.
//label/b[text()='Name']/ancestor::div[@class='input-container']/input
//label/b[text()='Name'] - locate any b element within label element with text 'Name'
//label/b[text()='Name']/ancestor::div[@class='input-container'] locate any parent or ancestor div which class equals 'input-container'
And then locate the input element inside the div
ii.
//label[b[text()='Name']]/following-sibling::input
//label[b[text()='Name']] - Locate any label with b element and in which text is equal 'Name'
//label[b[text()='Name']]/following-sibling::input - Then locate the next input element of the label, so we got the input element

12. Absolute XPath vs Relative XPath
Absolute XPath — Starting from root to your target element
Relative XPath — Starting from any point to your target element
Always use relative XPath as absolute XPath difficult to maintain, anything changed between the root element and the target element may require to update the XPath too.

13. Avoid overuse of index
Same reason as above, hard to maintain and hard to understand

Part 2 Tips in writing test case

  1. Reusable
    Modularize some the common test case procedure like login, logout.
    Modularize some common action, like click, onMouseOver, drop-down selection, element count, sum of columns and those function to read the value from different inputs.
    Reuse element location, like those elements in the same form, their locators can share the same starting string.
  2. Maintainable
    It is expected that the scope and user requirement are keep changing especially in agile environment which facilitate short delivery times. When we composing our test case, we should consider the cost of maintain it afterwards.
    For example, let say we have a form which contains more than 100 input elements.
    There are several ways to locate the elements
    i. locate by index
    ii. locate by label with ancestor or sibling syntax
    If we locate element by index, that means adding or removing any field will require the update of locator of other elements too. So, in this case, locate those elements by label would be much better
  3. Implicit Wait vs Explicit Wait
    driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
    Implicit Wait is a system wide setting, it is a default time to wait until the Selenium give up and throw the elementNotFoundException
    WebDriverWait wait = new WebDriverWait(driver,30);
    wait.until(ExpectedConditions.visibilityOfElementLocated(
    By.xpath("//div[contains(text(),'Register')]")));
    Explicit Wait is targeting for a specific element, for example, waiting an element to appear or waiting an element to be editable.

--

--