Basics beginner fundamentals best-practices

Relative vs Absolute Paths

Understand why relative XPath expressions are essential for maintainable test automation.

Relative vs Absolute Paths

One of the most important decisions in XPath is choosing between absolute and relative paths. This choice can make or break the maintainability of your test suite.

Absolute Paths

An absolute path starts from the document root (/html) and specifies every step down to your target element:

/html/body/div/div/main/section/form/div[2]/input

Why Absolute Paths Are Problematic

  1. Fragile: Any change to the DOM structure breaks the path
  2. Long and unreadable: Hard to understand what element is being selected
  3. No semantic meaning: The path tells you nothing about the element’s purpose
  4. Maintenance nightmare: Updating tests after UI changes becomes tedious
Real-world example:

A developer wraps a section in a new div for styling purposes. Every absolute path targeting elements inside that section now breaks—potentially hundreds of test failures from a single, innocent change.

Relative Paths

A relative path uses // to start from any matching node, regardless of its position in the document:

//input[@data-testid='email']
//button[contains(text(), 'Submit')]
//form[@id='login']//input[@type='password']

Advantages of Relative Paths

  1. Resilient: Structure changes rarely affect them
  2. Semantic: Target elements by meaningful attributes
  3. Readable: Express intent clearly
  4. Maintainable: Easier to update when needed

Double Slash (//) Explained

The // operator means “select matching nodes anywhere in the document (or from the current context)“:

//input                    // All input elements anywhere
//form//input              // All inputs inside any form
//div[@class='card']//button  // Buttons inside card divs

Single vs Double Slash

SyntaxMeaningExample
/Direct child only/html/body - body must be direct child of html
//Any descendant//body - find body anywhere

Best Practices

1. Start with // Unless You Have a Reason Not To

// ✅ Good
//input[@name='email']

// ❌ Avoid
/html/body/div/form/input[@name='email']

2. Anchor to Stable Parent Elements When Needed

Sometimes you need context to avoid ambiguity:

// Too broad - might match multiple forms
//input[@name='email']

// Better - scoped to specific form
//form[@id='login']//input[@name='email']

3. Use Unique Identifiers When Available

Preference order for attributes:

  1. data-testid, data-test, data-cy
  2. id (if not auto-generated)
  3. name (for form elements)
  4. aria-label, role
  5. Text content
  6. Class names (semantic only)

4. Avoid Position-Based Selection

// ❌ Fragile - position might change
//ul/li[3]

// ✅ Better - uses meaningful attribute
//ul/li[@data-item='featured']

// ✅ Or text content
//ul/li[contains(text(), 'Featured')]

When Absolute Paths Might Be Acceptable

There are rare cases where absolute-style paths make sense:

  1. Single-page applications with stable structure: If you control the DOM and it rarely changes
  2. Explicit scoping: //table[@id='results']//tr[1]/td[2] - relative to a known anchor
  3. Testing structural relationships: When the test specifically validates DOM structure

Even in these cases, anchor to a stable element first rather than starting from /html.

Practical Examples

Converting Absolute to Relative

Before (fragile):

/html/body/div[1]/main/div[2]/section/article[1]/header/h2

After (robust):

//article[@class='featured']//h2
// or
//article[1]//h2[@class='title']

Scoping Without Full Paths

Scenario: Select the “Add to Cart” button for a specific product

// Find the product card first, then the button within it
//div[@data-product-id='123']//button[text()='Add to Cart']

// Or using the product name
//article[.//h2[text()='Wireless Keyboard']]//button[contains(@class, 'add-to-cart')]

Try It Yourself

Compare these two XPaths on our sample e-commerce page:

Open in Playground →

Summary

AspectAbsolute PathRelative Path
Starts with/html/...//
StabilityVery fragileRobust
ReadabilityPoorGood
MaintenanceHigh effortLow effort
Use caseAlmost neverAlmost always

Remember: Your tests should survive routine UI updates. Relative paths make this possible.