Skip to content

Commit c4de630

Browse files
committed
Merge pull request #165 from helsing/issue_119_matchers
[#119] First implementation of hamcrest-matchers
2 parents 3c5f57f + 8421159 commit c4de630

File tree

17 files changed

+1068
-0
lines changed

17 files changed

+1068
-0
lines changed

json-path-assert/README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
json-path-assert
2+
================
3+
4+
A library with [hamcrest-matchers](http://hamcrest.org/JavaHamcrest/) for JsonPath.
5+
6+
# Getting started
7+
8+
This library is available at the Central Maven Repository. Maven users add this to your POM.
9+
10+
```xml
11+
<dependency>
12+
<groupId>com.jayway.jsonpath</groupId>
13+
<artifactId>json-path-assert</artifactId>
14+
<version>2.1.0</version>
15+
</dependency>
16+
```
17+
18+
# Usage guide
19+
20+
Statically import the library entry point:
21+
22+
import static com.jayway.jsonpath.matchers.JsonPathMatchers.*;
23+
24+
NOTE: The actual evaluation of JsonPath will depend on the current configuration:
25+
26+
Configuration.setDefaults(...);
27+
28+
The matchers can be used to inspect different representations of JSON:
29+
30+
// As a String...
31+
String json = ...;
32+
33+
// or a file...
34+
File json = ...;
35+
36+
// or an already parsed json object...
37+
Object json = Configuration.defaultConfiguration().jsonProvider().parse(content);
38+
39+
Usage examples:
40+
41+
// Verify validity of JSON
42+
assertThat(json, isJson());
43+
44+
// Verify existence (or non-existence) of JSON path
45+
assertThat(json, hasJsonPath("$.message"));
46+
assertThat(json, hasNoJsonPath("$.message"));
47+
48+
// Verify evaluation of JSON path
49+
assertThat(json, hasJsonPath("$.message", equalTo("Hi there")));
50+
assertThat(json, hasJsonPath("$.quantity", equalTo(5)));
51+
assertThat(json, hasJsonPath("$.price", equalTo(34.56)));
52+
assertThat(json, hasJsonPath("$.store.book[*].author", hasSize(4)));
53+
assertThat(json, hasJsonPath("$.store.book[*].author", hasItem("Evelyn Waugh")));
54+
55+
Combine matchers for greater expressiveness
56+
57+
// This will separate the JSON parsing from the path evaluation
58+
assertThat(json, isJson(withoutJsonPath("...")));
59+
assertThat(json, isJson(withJsonPath("...", equalTo(3))));
60+
61+
// Combine several JSON path evaluations into a single statement
62+
// (This will parse the JSON only once)
63+
assertThat(json, isJson(allOf(
64+
withJsonPath("$.store.name", equalTo("Little Shop")),
65+
withoutJsonPath("$.expensive"),
66+
withJsonPath("$..title", hasSize(4)))));
67+
68+
Match on pre-compiled complex JSON path expressions
69+
70+
Filter cheapFictionFilter = filter(
71+
where("category").is("fiction").and("price").lte(10D));
72+
JsonPath cheapFiction = JsonPath.compile("$.store.book[?]", cheapFictionFilter);
73+
String json = ...;
74+
assertThat(json, isJson(withJsonPath(cheapFiction)));
75+
76+
Use typed matchers for specific JSON representations, if needed
77+
78+
String json = ...
79+
assertThat(json, isJsonString(withJsonPath("$..author")));
80+
81+
File json = ...
82+
assertThat(json, isJsonFile(withJsonPath("$..author")));
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.jayway.jsonpath.matchers;
2+
3+
import com.jayway.jsonpath.JsonPath;
4+
import com.jayway.jsonpath.JsonPathException;
5+
import com.jayway.jsonpath.ReadContext;
6+
import org.hamcrest.Description;
7+
import org.hamcrest.Matcher;
8+
import org.hamcrest.TypeSafeMatcher;
9+
10+
import java.io.File;
11+
import java.io.IOException;
12+
13+
public class IsJson<T> extends TypeSafeMatcher<T> {
14+
private final Matcher<? super ReadContext> jsonMatcher;
15+
16+
public IsJson(Matcher<? super ReadContext> jsonMatcher) {
17+
this.jsonMatcher = jsonMatcher;
18+
}
19+
20+
@Override
21+
protected boolean matchesSafely(T json) {
22+
try {
23+
ReadContext context = parse(json);
24+
return jsonMatcher.matches(context);
25+
} catch (JsonPathException e) {
26+
return false;
27+
} catch (IOException e) {
28+
return false;
29+
}
30+
}
31+
32+
public void describeTo(Description description) {
33+
description.appendText("is json ").appendDescriptionOf(jsonMatcher);
34+
}
35+
36+
@Override
37+
protected void describeMismatchSafely(T json, Description mismatchDescription) {
38+
try {
39+
ReadContext context = parse(json);
40+
jsonMatcher.describeMismatch(context, mismatchDescription);
41+
} catch (JsonPathException e) {
42+
buildMismatchDescription(json, mismatchDescription, e);
43+
} catch (IOException e) {
44+
buildMismatchDescription(json, mismatchDescription, e);
45+
}
46+
}
47+
48+
private static void buildMismatchDescription(Object json, Description mismatchDescription, Exception e) {
49+
mismatchDescription
50+
.appendText("was ")
51+
.appendValue(json)
52+
.appendText(" which failed with ")
53+
.appendValue(e.getMessage());
54+
}
55+
56+
private static ReadContext parse(Object object) throws IOException {
57+
if (object instanceof String) {
58+
return JsonPath.parse((String) object);
59+
} else if (object instanceof File) {
60+
return JsonPath.parse((File) object);
61+
} else {
62+
return JsonPath.parse(object);
63+
}
64+
}
65+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.jayway.jsonpath.matchers;
2+
3+
import com.jayway.jsonpath.JsonPath;
4+
import com.jayway.jsonpath.Predicate;
5+
import com.jayway.jsonpath.ReadContext;
6+
import org.hamcrest.Matcher;
7+
8+
import java.io.File;
9+
10+
import static org.hamcrest.Matchers.*;
11+
12+
public class JsonPathMatchers {
13+
14+
private JsonPathMatchers() {
15+
throw new AssertionError("prevent instantiation");
16+
}
17+
18+
public static Matcher<? super Object> hasJsonPath(String jsonPath) {
19+
return describedAs("has json path %0",
20+
hasJsonPath(jsonPath, not(anyOf(nullValue(), empty()))),
21+
jsonPath);
22+
}
23+
24+
public static <T> Matcher<? super Object> hasJsonPath(final String jsonPath, final Matcher<T> resultMatcher) {
25+
return isJson(withJsonPath(jsonPath, resultMatcher));
26+
}
27+
28+
public static Matcher<? super Object> hasNoJsonPath(String jsonPath) {
29+
return isJson(withoutJsonPath(jsonPath));
30+
}
31+
32+
public static Matcher<Object> isJson() {
33+
return isJson(withJsonPath("$..*"));
34+
}
35+
36+
public static Matcher<Object> isJson(final Matcher<? super ReadContext> matcher) {
37+
return new IsJson<Object>(matcher);
38+
}
39+
40+
public static Matcher<String> isJsonString(final Matcher<? super ReadContext> matcher) {
41+
return new IsJson<String>(matcher);
42+
}
43+
44+
public static Matcher<File> isJsonFile(final Matcher<? super ReadContext> matcher) {
45+
return new IsJson<File>(matcher);
46+
}
47+
48+
public static Matcher<? super ReadContext> withJsonPath(String jsonPath, Predicate... filters) {
49+
return withJsonPath(JsonPath.compile(jsonPath, filters));
50+
}
51+
52+
public static Matcher<? super ReadContext> withJsonPath(JsonPath jsonPath) {
53+
return describedAs("with json path %0",
54+
withJsonPath(jsonPath, not(anyOf(nullValue(), empty()))),
55+
jsonPath.getPath());
56+
}
57+
58+
public static Matcher<? super ReadContext> withoutJsonPath(String jsonPath, Predicate... filters) {
59+
return withoutJsonPath(JsonPath.compile(jsonPath, filters));
60+
}
61+
62+
public static Matcher<? super ReadContext> withoutJsonPath(JsonPath jsonPath) {
63+
return new WithoutJsonPath(jsonPath);
64+
}
65+
66+
public static <T> Matcher<? super ReadContext> withJsonPath(String jsonPath, Matcher<T> resultMatcher) {
67+
return withJsonPath(JsonPath.compile(jsonPath), resultMatcher);
68+
}
69+
70+
public static <T> Matcher<? super ReadContext> withJsonPath(final JsonPath jsonPath, final Matcher<T> resultMatcher) {
71+
return new WithJsonPath<T>(jsonPath, resultMatcher);
72+
}
73+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.jayway.jsonpath.matchers;
2+
3+
import com.jayway.jsonpath.JsonPath;
4+
import com.jayway.jsonpath.JsonPathException;
5+
import com.jayway.jsonpath.PathNotFoundException;
6+
import com.jayway.jsonpath.ReadContext;
7+
import org.hamcrest.Description;
8+
import org.hamcrest.Matcher;
9+
import org.hamcrest.TypeSafeMatcher;
10+
11+
public class WithJsonPath<T> extends TypeSafeMatcher<ReadContext> {
12+
private final JsonPath jsonPath;
13+
private final Matcher<T> resultMatcher;
14+
15+
public WithJsonPath(JsonPath jsonPath, Matcher<T> resultMatcher) {
16+
this.jsonPath = jsonPath;
17+
this.resultMatcher = resultMatcher;
18+
}
19+
20+
@Override
21+
protected boolean matchesSafely(ReadContext context) {
22+
try {
23+
T value = context.read(jsonPath);
24+
return resultMatcher.matches(value);
25+
} catch (JsonPathException e) {
26+
return false;
27+
}
28+
}
29+
30+
public void describeTo(Description description) {
31+
description
32+
.appendText("with json path ")
33+
.appendValue(jsonPath.getPath())
34+
.appendText(" evaluated to ")
35+
.appendDescriptionOf(resultMatcher);
36+
}
37+
38+
@Override
39+
protected void describeMismatchSafely(ReadContext context, Description mismatchDescription) {
40+
try {
41+
T value = jsonPath.read(context.json());
42+
mismatchDescription
43+
.appendText("json path ")
44+
.appendValue(jsonPath.getPath())
45+
.appendText(" was evaluated to ")
46+
.appendValue(value);
47+
} catch (PathNotFoundException e) {
48+
mismatchDescription
49+
.appendText("json path ")
50+
.appendValue(jsonPath.getPath())
51+
.appendText(" was not found in ")
52+
.appendValue(context.json());
53+
} catch (JsonPathException e) {
54+
mismatchDescription
55+
.appendText("was ")
56+
.appendValue(context.json());
57+
}
58+
}
59+
60+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.jayway.jsonpath.matchers;
2+
3+
import com.jayway.jsonpath.JsonPath;
4+
import com.jayway.jsonpath.JsonPathException;
5+
import com.jayway.jsonpath.ReadContext;
6+
import org.hamcrest.Description;
7+
import org.hamcrest.TypeSafeDiagnosingMatcher;
8+
9+
import static org.hamcrest.Matchers.empty;
10+
11+
public class WithoutJsonPath extends TypeSafeDiagnosingMatcher<ReadContext> {
12+
private final JsonPath jsonPath;
13+
14+
public WithoutJsonPath(JsonPath jsonPath) {
15+
this.jsonPath = jsonPath;
16+
}
17+
18+
@Override
19+
protected boolean matchesSafely(ReadContext actual, Description mismatchDescription) {
20+
try {
21+
Object value = actual.read(jsonPath);
22+
mismatchDescription
23+
.appendText(jsonPath.getPath())
24+
.appendText(" was evaluated to ")
25+
.appendValue(value);
26+
return value == null || empty().matches(value);
27+
} catch (JsonPathException e) {
28+
return true;
29+
}
30+
}
31+
32+
@Override
33+
public void describeTo(Description description) {
34+
description.appendText("without json path ").appendValue(jsonPath.getPath());
35+
}
36+
}

0 commit comments

Comments
 (0)