DEV Community

Volta Jebaprashanth
Volta Jebaprashanth

Posted on

Raw XPath is Dead: How XPathy's Fluent API Solves Your Flaky Test Problem

One of the most frequent causes of flaky tests is when a front-end change inadvertently alters the casing or spacing of an HTML attribute or text content. A CSS class might change from active to Active, or an error message might gain an extra space: " Error Message " vs. "Error Message". These minor variations are enough to break an exact-match XPath, leading to frustrating test failures.

XPathy's Value Transformation feature provides a fluent, built-in mechanism to address these issues. By applying transformations like case-folding and space-normalization before the locator comparison, XPathy ensures your locators match the intended value, regardless of the developer's formatting choices.


1. Achieving Case-Insensitive Matching

Raw XPath requires the complex use of the translate() function to achieve case-insensitivity. You must provide the entire alphabet twice: once for upper-case characters to replace, and once for the lower-case equivalents to replace them with.

Raw, Error-Prone XPath
//div[contains(translate(@class, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'menu-item')]

XPathy simplifies this into a single, declarative method call using the Case enum.

The .withCase(IGNORED) Method

The most common transformation is to ignore case for stable attributes like id or class.

import static com.xpathy.Case.*;

XPathy locator = div.byAttribute(class_)
                    .withCase(IGNORED)
                    .equals("active-tab");

// Result:
//div[translate(@class, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='active-tab']
Enter fullscreen mode Exit fullscreen mode

This locator will successfully match elements with class="active-tab", class="Active-Tab", or class="ACTIVE-TAB". This small addition instantly makes the locator bulletproof against casing inconsistencies.

Force Casing (UPPER and LOWER)

You can also force the element's value to a specific case before comparison, which is helpful for standardizing text content:

XPathy locator = label.byText()
                    .withCase(UPPER)
                    .equals("USERNAME");

// Result:
//label[translate(text(), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')='USERNAME']
Enter fullscreen mode Exit fullscreen mode

This ensures the locator matches any variation of "Username," "username," or "USERNAME."


2. Whitespace and Symbol Cleanup

Inconsistent spacing in text nodes is a rampant source of locator flakiness. Similarly, needing to compare numeric values (like prices) often requires removing currency symbols.

Normalize Space with .withNormalizeSpace()

The XPath normalize-space() function strips leading and trailing whitespace and replaces sequences of internal whitespace with a single space. XPathy exposes this critical utility directly.

XPathy locator = div.byText()
                    .withNormalizeSpace()
                    .equals("Invalid password");

// Result:
//div[normalize-space(text())='Invalid password']
Enter fullscreen mode Exit fullscreen mode

This locator will match text nodes that contain "Invalid password", " Invalid password ", or even "Invalid password".

Filtering Characters for Consistency

When dealing with dynamic content like prices or order IDs, you often only care about the alphanumeric parts. XPathy allows you to explicitly keep or remove sets of characters.

Scenario XPathy Code Function
Numeric Value Check span.byText().withRemoveOnly(SPECIAL_CHARACTERS).contains("1999") Removes $ or , to stabilize price comparison.
ID Clean-up td.byText().withKeepOnly(ENGLISH_ALPHABETS, NUMBERS).equals("ORD123") Removes hyphens or spaces from Order IDs like ORD-123 before checking.

These transformations allow you to focus on the meaningful data rather than the superficial presentation or formatting characters, making the locator invariant to regional currency or spacing differences.


3. Combining Transformations for Ultimate Resilience

The true power of XPathy lies in chaining these operations to clean up a value thoroughly before comparison.

Imagine a product title that might have inconsistent spacing, numbers (which should be ignored), and mixed casing.

XPathy locator = div.byText()
                    .withNormalizeSpace()
                    .withRemoveOnly(NUMBERS)
                    .withCase(IGNORED)
                    .contains("premium feature");

// Result:
//div[contains(translate(translate(normalize-space(text()), '0123456789', ''), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'premium feature')]
Enter fullscreen mode Exit fullscreen mode

By stacking .withNormalizeSpace(), .withRemoveOnly(), and .withCase(), the XPath is comprehensively cleaned up. The final result is a locator that will reliably find the element, regardless of how many formatting inconsistencies are introduced by the UI layer.

These fluent transformations are the key to building locators that are truly bulletproof against the casing, spacing, and formatting issues that plague Selenium test stability.

XPathy provides a lot more: Read the Full Documentation:

https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753

The Repository:

https://github.com/Volta-Jebaprashanth/xpathy

Happy Testing! 🚀

Top comments (0)