Automation

XPath for QA Interviews: Syntax, Examples and Common Questions

A concise XPath interview guide for QA engineers with practical HTML examples, axes, predicates, dynamic locators, common mistakes, and stable-selector advice.

JobFitPilot Editorial · Practical career guides13 min read
In this guide

XPath selects nodes by navigating a document tree. In UI automation it is useful when a stable ID, accessible locator, or dedicated test attribute is unavailable and the relationship between elements is meaningful.

Interviewers usually want syntax plus judgment: can you write a precise locator, explain why it works, and avoid a brittle path tied to the current layout?

Use this sample HTML for the examples

Example markup
<section id="checkout">
  <h2>Payment</h2>
  <form data-testid="payment-form">
    <label for="email">Email</label>
    <input id="email" name="email" type="email" />
    <button class="btn primary" type="submit">Pay now</button>
  </form>
  <p class="status" data-state="pending">Waiting</p>
</section>

In real automation, prefer a unique stable locator such as id, an accessible role/name supported by the tool, or a deliberate data-testid. Use XPath when it expresses a stable attribute or relationship more clearly than the alternatives.

Absolute and relative XPath

An absolute path starts at the document root and describes every level. A relative XPath can search from the current context and is usually easier to keep stable.

Absolute then relative XPath
/html/body/main/section/form/button
//button[@type="submit"]

Understand /, //, @, and predicates

Core XPath syntax used in UI locators
SyntaxMeaningExample
/Select a direct child step//form/button
//Select matching descendants from the current context//section//input
@Refer to an attribute//input[@name="email"]
[...]Filter the selected node set//button[@type="submit"]
*Match any element node in the step//*[@data-testid="payment-form"]

// is convenient, but a broad search such as //* can be slow and ambiguous. Add a stable element name, attribute, or container when it improves precision.

Match text carefully

Text matching examples
//button[text()="Pay now"]
//button[normalize-space(.)="Pay now"]
//h2[contains(normalize-space(.), "Payment")]

text() targets direct text nodes, while . represents the string value of the current element including descendant text. normalize-space(.) is useful when harmless surrounding whitespace varies. Text locators may break under copy changes or localization, so use them only when the visible label is the intended contract.

Use contains() and starts-with() for deliberate partial matches

Partial-match examples
//button[contains(@class, "primary")]
//input[starts-with(@name, "email")]
//*[@data-state and starts-with(@data-state, "pend")]

Partial matching helps with controlled prefixes or compound attributes, but it can match unintended values. For an HTML class token, a simple substring can confuse primary with primary-alt; a stable dedicated attribute is clearer when the application can provide one.

Combine conditions with and or

Boolean predicate examples
//input[@name="email" and @type="email"]
//button[@type="submit" or @data-action="submit-payment"]

and narrows the same candidate to nodes meeting both conditions. or accepts either condition and should be used only when both forms are legitimately supported, not to hide unstable markup.

Navigate relationships with axes

Axes express a relationship from one node to another. They are valuable when the stable information belongs to a nearby label, row, card, or container rather than the target control.

Axis examples
//form/child::button[@type="submit"]
//input[@id="email"]/parent::form
//h2[normalize-space(.)="Payment"]/following-sibling::form
//button[@type="submit"]/ancestor::form
//p[@class="status"]/preceding-sibling::form
Common relationship axes
AxisUse
child::Select a direct child
parent::Select the direct parent
ancestor::Select a parent at any higher level
following-sibling::Select later siblings with the same parent
preceding-sibling::Select earlier siblings with the same parent
following::Select later nodes in document order

Use indexing with the intended scope

Two different index scopes
(//button[@type="submit"])[1]
//form[@data-testid="payment-form"]/button[1]

Parentheses make [1] apply to the complete result set. Without them, [1] can mean the first matching child for each parent in that step. Indexing is fragile when order can change, so first look for a stable distinguishing attribute or container.

Handle dynamic elements without guessing

A dynamic locator should rely on the stable part of the contract: a prefix, semantic attribute, associated label, or stable container. Do not automatically strip a changing suffix if the remaining prefix matches several elements.

Stable contextual locators
//section[@id="checkout"]//input[@name="email"]
//*[@data-testid="payment-form"]//button[@type="submit"]

Avoid common XPath mistakes

  • Do not copy a long absolute XPath from browser tools without understanding it.
  • Do not use an index to hide the fact that a locator matches multiple unrelated elements.
  • Do not assume contains(@class, "x") performs exact class-token matching.
  • Do not depend on visible text when localization or routine copy edits are expected.
  • Do not search the entire page when a stable component container can scope the locator.
  • Do not replace stable IDs, accessible locators, or test attributes with XPath merely to demonstrate XPath.
  • Always verify uniqueness and the intended target in the real rendered DOM.
Question 11

What is XPath syntax?

Short answer

XPath syntax describes a path through a document tree. A location step can specify an axis, a node test, and predicates. In //input[@name="email"], // searches descendants, input is the node test, and [@name="email"] filters by an attribute. I prefer stable IDs, accessible locators, or test attributes when available, and use XPath for stable attributes or element relationships.

Interview-ready syntax patterns
//element[@attribute="value"]
//container//element[condition]
//label[normalize-space(.)="Name"]/following::input[1]

Related guides