Introducing fluentXPath

java

Some time ago I’ve started working on updating automated tests written in selenium with java. After working a bit with it I’ve noticed that from time to time constructing XPath expression can get ugly. We are writing our tests in pure java and "ugliness" usually comes from string concatenation when some extra parameters must be taken into account. I’ve decided to write something that might help a bit with building XPaths without string concatenation.

tl;dr

I’ve created fluentXPath library which allows to build XPath expression without concatenating strings and provides fluent syntax to construct XPaths.

No warranty of any kind

Note that does not offer anything but builder for XPaths. It doesn’t check if your XPath is valid it’s nothing but syntactic sugar so you don’t have to concatenate strings. If you are using groovy or Kotlin or any other JVM language with string interpolation you should probably use interpolation instead of fluentXPath. Except from a bit of suggar there is nothing extra that you wouldn’t achieve with simple string concatenation and unless your expression are complex you should be fine with simple "a" + "b".

Why?

Firstly I don’t like to concatenate strings and for now, I’ll have to use pure java for automated tests. The second reason was to learn and understand better complex XPath expressions.

Examples

String builderCommentsLink = xpathOf()
    .anyElement("div")
    .has(
            xpathFn().and(asList(
                    xpathFn().eq(xpathAttribute("data-test-article-id"), xpathValue("123")),
                    xpathFn().contains(
                        xpathFn().lowerCase(xpathAttribute("data-test-article-category")),
                        xpathValue("News")))))
    .descendantElement("span")
    .has(
            xpathFn().contains(
                    xpathAttribute("class"),
                    xpathValue("links")))
    .descendantElement("a")
    .has(
        xpathFn().contains(
            xpathFn().lowerCase(xpathFn().text()),
            xpathValue("comments")))
    .build();

You can write something more complex which allows to build XPaths more dynamically:

Stream<String> newArticleIds = Stream.of("1","2","3","4");

private static List<XPathExpression> articleIdIn(Stream<String> newArticleIds) {
    return newArticleIds
        .map(singleArticleId -> xpathFn().eq(
                xpathAttribute("data-test-article-id"),
                xpathValue(singleArticleId)))
        .collect(Collectors.toList());
}

xpathOf()
    .anyElement("div")
    .has(
            xpathFn().and(asList(
                    xpathFn().or(articleIdIn(newArticleIds)),
                    xpathFn().contains(
                        xpathFn().lowerCase(xpathAttribute("data-test-article-category")),
                        xpathValue("News")))))
    .descendantElement("span")
    .has(
            xpathFn().contains(
                    xpathAttribute("class"),
                    xpathValue("links")))
    .descendantElement("a")
    .has(xpathFn().contains(
        xpathFn().lowerCase(xpathFn().text()),
        xpathValue("comments")))
    .build();

Of course many functions are still missing but you are free to define them on your own (or create something that you’ll be able to use like aliases).

private static class XPathCount implements XPathExpression {
    private final XPathExpression expression;

    private XPathCount(XPathExpression expression) {
        this.expression = expression;
    }

    @Override
    public String build() {
        return "count((" +
                expression.build() +
                "))";
    }
}

xpathFn().greaterThan(
        new XPathCount(xpathOf()
            .anyElement("a")
            .has(xpathFn().contains(
                xpathAttribute("class"),
                xpathValue("links")))),
        xpathValue(4))
    .build();

More examples:

How to get it

Just add dependencies to pom.xml:

<dependency>
  <groupId>com.pchudzik</groupId>
  <artifactId>fluentxpath</artifactId>
  <version>1.0.0</version>
</dependency>

or to build.gradle:

compile "com.pchudzik:fluentxpath:1.0.0"

To find out latest available version you check out readme file. If you decide to give it a try please let me know about any bugs I’ve missed. Don’t hesitate to request new features there is a good chance that I’ll implement them.

Alternatives

22 Jan 2019 #java #testing