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.