I couldn’t find a Hamcrest matcher for JSON that worked how I wanted, so I adapted the JsonAssert library.
This allows you to use dot notation to specify the path to a node in a json string, and use any Hamcrest matcher to check the contents of the string at that node.
For example, given a Json string like:
{"someThing": {
"innerThing": {
"partOne": "abc",
"partTwo": 123
}
}}
You could call
assertThat(jsonString,
hasJsonPath("someThing.innerThing.partOne", equalTo("abc")));
assertThat(jsonString,
hasJsonPath("someThing.innerThing.partOne", greaterThan(100)));
That’s not massively different to the syntax you get with JsonAssert, but I prefer it. The equivalent with JsonAssert would be
with(jsonString)
.assertThat("someThing.innerThing.partOne", equalTo("abc"));
which doesn’t seem quite so fluent.
Note that the matcher you use for the value of the required node must be a matcher for the unmarshalled type.
In other words, if the node contains “abc” then a Matcher<String> can be used, and if it contained 123 then a Matcher<Integer> could be used.
import static com.jayway.jsonassert.JsonAssert.with;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
public class JsonMatchers {
public static <T> Matcher<String> hasJsonPath(
final String jsonPath, final Matcher<T> matches) {
return new TypeSafeMatcher<String>() {
@Override
protected boolean matchesSafely(String json) {
try {
with(json).assertThat(jsonPath, matches);
return true;
} catch (AssertionError ae) {
return false;
} catch (Exception e) {
return false;
}
}
@Override
public void describeTo(Description description) {
description
.appendText(" JSON object with a value at node ")
.appendValue(jsonPath)
.appendText(" that is ")
.appendDescriptionOf(matches);
}
};
}
}
OK, that’s a bit of a quick hack, and it looks very ugly formatted to fit this page, but it does the job. It gives you easy to read assertions without a load of awkward engineering just to deal with the json, or clumsy string matching that isn’t precise enough and causes tests to be brittle and not expressive.
Hamcrest is cool.
PS you’ll need the json-path-assert dependency
<dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path-assert</artifactId> <version>0.9.1</version> <scope>test</scope> </dependency>
wow clear !!!
This is very helpful series of articles. Very nice job