This is a mobile version, full one is here.
Yegor Bugayenko
28 April 2014
XML/XPath Matchers for Hamcrest
Hamcrest is my favorite instrument
in unit testing. It replaces the JUnit procedural assertions of
org.junit.Assert
with an object-oriented mechanism. However, I will discuss
that subject in more detail sometime later.
Now, though, I want to demonstrate a new library published today on GitHub and Maven Central: jcabi-matchers. jcabi-matchers is a collection of Hamcrest matchers to make XPath assertions in XML and XHTML documents.
Let’s say, for instance, a class that is undergoing testing produces an XML
that needs to contain a single <message>
element with the content "hello,
world!"
This is how that code would look in a unit test:
import com.jcabi.matchers.XhtmlMatchers;
import org.hamcrest.MatcherAssert;
import org.junit.Test;
public class FooTest {
@Test
public void hasWelcomeMessage() {
MatcherAssert.assertThat(
new Foo().createXml(),
XhtmlMatchers.hasXPaths(
"/document[count(message)=1]",
"/document/message[.='hello, world!']"
)
);
}
}
There are two alternatives to the above that I’m aware of, which are do almost the same thing:
xml-matchers by
David Ehringer
and
hasXPath()
method in Hamcrest itself.
I have tried them both, but faced a number of problems.
First, Hamcrest hasXPath()
works only with an instance of
Node
. With
this method, converting a String
into Node
becomes a repetitive and routine
task in every unit test.
The above is a very strange limitation of Hamcrest in contrast to
jcabi-matchers, which works with almost anything, from a
String
to a
Reader
and
even an InputStream
.
Second, XmlMatchers
from xml-matchers
provides a very inconvenient way for working with namespaces. Before you can
use an XPath query with a non-default namespace, you should create an instance
of NamespaceContext
.
The library provides a simple implementation of this interface, but, still, it is requires extra code in every unit test.
jcabi-matchers simplifies namespace handling
problems even further, as it pre-defines most popular namespaces, including
xhtml
, xs
, xsl
, etc.
The following example works right out-of-the-box—without any extra configuration:
MatcherAssert.assertThat(
new URL("https://www.google.com").getContent(),
XhtmlMatchers.hasXPath("//xhtml:body")
);
To summarize, my primary objective with the library was its simplicity of usage.