DEV Community

Cover image for From String Soup to Fluent Code: Reinventing XPath in Java with XPathy 🚀
Volta Jebaprashanth
Volta Jebaprashanth

Posted on

From String Soup to Fluent Code: Reinventing XPath in Java with XPathy 🚀

If you’ve spent any time writing UI automation with Selenium and Java, you know the frustration: XPath is brittle, hard to read, and a nightmare to maintain. You spend precious minutes balancing quotes, checking for typos, and debugging flaky locators that break every time a developer changes a single CSS class.

We call this "String Soup"—a mess of long, error-prone strings that hide your automation intent.

But what if you could write locators that look like clean, fluent Java code? What if they could adapt automatically to whitespace, casing, and special characters?

Meet XPathy, a lightweight Java library designed to take the pain out of XPath.


🔍 The Problem: Why XPath Strings Fail Us

Consider a common scenario: you need to find a submit button that has a specific ID but is not currently disabled, and its text should be case-insensitive.

❌ The String Soup XPath

//button[contains(@id, 'submit-') and not(contains(translate(@class, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'disabled')) and translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='login']
Enter fullscreen mode Exit fullscreen mode

This single line is:

  • Unreadable: Good luck figuring out the logic in six months.
  • Error-Prone: Missing a quote, forgetting a bracket, or mistyping translate is easy.
  • Untrustworthy: It relies on complex functions and manual string management.

âś… The Solution: Fluent and Intent-Driven Code

XPathy replaces raw strings with a fluent API that maps human intent directly to XPath logic. Instead of manually writing contains() or translate(), you simply chain methods.

âś… The XPathy Fluent Code

import static com.xpathy.Tag.*;
import static com.xpathy.Attribute.*;
import static com.xpathy.Case.*;

XPathy locator = button.byAttribute(id).contains("submit-")
                        .and()
                        .byAttribute(class_).not().withCase(IGNORED).contains("disabled")
                        .and()
                        .byText().withCase(IGNORED).equals("login");
Enter fullscreen mode Exit fullscreen mode

Result:

//button[contains(@id, 'submit-') and not(contains(translate(@class, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'disabled')) and translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='login']
Enter fullscreen mode Exit fullscreen mode

The code is now:

  • Readable: It reads like an English sentence.
  • Type-Safe: The compiler catches typos.
  • Maintainable: Add or remove conditions without breaking anything.

🔪 Three Pillars of Robust Locators

XPathy solves the three biggest problems with locators: brittleness, complexity, and ambiguity.

1. Bulletproof Locators with Transformations 🛡️

The most common reason for flaky tests is inconsistency in UI values (whitespace, case, or formatting). XPathy’s Transformations automatically wrap your value in the necessary XPath functions.

Flaky Scenario XPathy Solution Fluent Code
Case Inconsistency: Login vs LOGIN withCase(IGNORED) byText().withCase(IGNORED).equals("login")
Whitespace Padding: " Invalid Password " withNormalizeSpace() byText().withNormalizeSpace().equals("Invalid Password")
Special Characters: $1,999.00 withRemoveOnly(SPECIAL_CHARACTERS) byText().withRemoveOnly(SPECIAL_CHARACTERS).equals("199900")
Accents/Diacritics: Café vs Cafe withTranslate() byText().withTranslate("éàè", "eae").contains("Cafe")

2. Mastering Complex Logic and Precedence đź§ 

When a simple .and() isn’t enough, XPathy gives you explicit logical control.

A. Grouping Conditions with Union and Intersect

// Match a button that has one of three possible dynamic IDs
XPathy locator = button.byAttribute(id)
                       .union(
                           Or.equals("login-btn"),
                           Or.equals("signin-btn"),
                           Or.contains("auth-")
                       );
Enter fullscreen mode Exit fullscreen mode

Result:

//button[@id='login-btn' or @id='signin-btn' or contains(@id, 'auth-')]
Enter fullscreen mode Exit fullscreen mode

B. Nested Logic for Precision

import static com.xpathy.Condition.*;

XPathy locator = div.byCondition(
    and(
        text().contains("Order"),
        or(
            attribute(class_).equals("pending"),
            attribute(class_).equals("processing")
        )
    )
);
Enter fullscreen mode Exit fullscreen mode

Result:

//div[contains(text(), 'Order') and (@class='pending' or @class='processing')]
Enter fullscreen mode Exit fullscreen mode

3. DOM Relationships and Having Operations 🔍

Defining relationships in XPath is powerful but hard to read. XPathy simplifies this with byHaving().

// Find a <div> product card that *has* a descendant button with "Add to Cart" text
XPathy locator = div.byAttribute(class_).equals("product-card")
                     .and()
                     .byHaving().descendant(
                         button.byText().contains("Add to Cart")
                     );
Enter fullscreen mode Exit fullscreen mode

Result:

//div[@class='product-card' and ( .//button[contains(text(), 'Add to Cart')] )]
Enter fullscreen mode Exit fullscreen mode

This makes it effortless to filter parent elements based on the content of their descendants.


🚀 From XPathy to Selenium

XPathy integrates directly with Selenium—no extra setup.

Get the Selenium Locator:

By seleniumLocator = xpathy.getLocator();
driver.findElement(seleniumLocator).click();
Enter fullscreen mode Exit fullscreen mode

Get the Raw XPath String (for logging/debugging):

String rawXPath = xpathy.toString();
Enter fullscreen mode Exit fullscreen mode

📊 Conclusion: Stop Writing Strings, Start Writing Code

XPathy is more than a utility—it’s a paradigm shift for locator management. It turns fragile XPath strings into expressive, fluent, and type-safe Java code.

If your tests constantly break because of small UI changes, it’s time to stop debugging String Soup and start building robust, readable, and maintainable locators with XPathy.

Ready to start? Check out the full documentation and repository.

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)