Skip to content

Commit 35dd307

Browse files
committed
Merge pull request #854 from candrews/SPR-13320
* SPR-13320: Introduce additional JsonPath matchers in Spring MVC Test
2 parents 48b965a + fffdd1e commit 35dd307

File tree

5 files changed

+293
-104
lines changed

5 files changed

+293
-104
lines changed

spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java

Lines changed: 119 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,21 @@
2020
import java.lang.reflect.Method;
2121
import java.text.ParseException;
2222
import java.util.List;
23+
import java.util.Map;
2324

24-
import com.jayway.jsonpath.InvalidPathException;
25-
import com.jayway.jsonpath.JsonPath;
2625
import org.hamcrest.Matcher;
2726

2827
import org.springframework.util.Assert;
2928
import org.springframework.util.ReflectionUtils;
3029

31-
import static org.hamcrest.MatcherAssert.*;
32-
import static org.springframework.test.util.AssertionErrors.*;
30+
import com.jayway.jsonpath.InvalidPathException;
31+
import com.jayway.jsonpath.JsonPath;
32+
33+
import static org.hamcrest.MatcherAssert.assertThat;
34+
import static org.hamcrest.core.IsInstanceOf.instanceOf;
35+
import static org.springframework.test.util.AssertionErrors.assertEquals;
36+
import static org.springframework.test.util.AssertionErrors.assertTrue;
37+
import static org.springframework.test.util.AssertionErrors.fail;
3338

3439
/**
3540
* A helper class for applying assertions via JSON path expressions.
@@ -39,6 +44,8 @@
3944
*
4045
* @author Rossen Stoyanchev
4146
* @author Juergen Hoeller
47+
* @author Craig Andrews
48+
* @author Sam Brannen
4249
* @since 3.2
4350
*/
4451
public class JsonPathExpectationsHelper {
@@ -69,92 +76,130 @@ public class JsonPathExpectationsHelper {
6976

7077

7178
/**
72-
* Construct a new JsonPathExpectationsHelper.
73-
* @param expression the JsonPath expression
74-
* @param args arguments to parameterize the JSON path expression with
79+
* Construct a new {@code JsonPathExpectationsHelper}.
80+
* @param expression the {@link JsonPath} expression; never {@code null} or empty
81+
* @param args arguments to parameterize the {@code JsonPath} expression, with
7582
* formatting specifiers defined in {@link String#format(String, Object...)}
7683
*/
7784
public JsonPathExpectationsHelper(String expression, Object... args) {
85+
Assert.hasText(expression, "expression must not be null or empty");
7886
this.expression = String.format(expression, args);
7987
this.jsonPath = (JsonPath) ReflectionUtils.invokeMethod(
8088
compileMethod, null, this.expression, emptyFilters);
8189
}
8290

8391

8492
/**
85-
* Evaluate the JSON path and assert the resulting value with the given {@code Matcher}.
86-
* @param content the response content
87-
* @param matcher the matcher to assert on the resulting json path
93+
* Evaluate the JSON path expression against the supplied {@code content}
94+
* and assert the resulting value with the given {@code Matcher}.
95+
* @param content the JSON response content
96+
* @param matcher the matcher with which to assert the result
8897
*/
8998
@SuppressWarnings("unchecked")
9099
public <T> void assertValue(String content, Matcher<T> matcher) throws ParseException {
91100
T value = (T) evaluateJsonPath(content);
92-
assertThat("JSON path " + this.expression, value, matcher);
93-
}
94-
95-
private Object evaluateJsonPath(String content) throws ParseException {
96-
String message = "No value for JSON path: " + this.expression + ", exception: ";
97-
try {
98-
return this.jsonPath.read(content);
99-
}
100-
catch (InvalidPathException ex) {
101-
throw new AssertionError(message + ex.getMessage());
102-
}
103-
catch (ArrayIndexOutOfBoundsException ex) {
104-
throw new AssertionError(message + ex.getMessage());
105-
}
106-
catch (IndexOutOfBoundsException ex) {
107-
throw new AssertionError(message + ex.getMessage());
108-
}
101+
assertThat("JSON path \"" + this.expression + "\"", value, matcher);
109102
}
110103

111104
/**
112-
* Apply the JSON path and assert the resulting value.
105+
* Evaluate the JSON path expression against the supplied {@code content}
106+
* and assert that the result is equal to the expected value.
107+
* @param content the JSON response content
108+
* @param expectedValue the expected value
113109
*/
114-
public void assertValue(String responseContent, Object expectedValue) throws ParseException {
115-
Object actualValue = evaluateJsonPath(responseContent);
110+
public void assertValue(String content, Object expectedValue) throws ParseException {
111+
Object actualValue = evaluateJsonPath(content);
116112
if ((actualValue instanceof List) && !(expectedValue instanceof List)) {
117113
@SuppressWarnings("rawtypes")
118114
List actualValueList = (List) actualValue;
119115
if (actualValueList.isEmpty()) {
120116
fail("No matching value for JSON path \"" + this.expression + "\"");
121117
}
122118
if (actualValueList.size() != 1) {
123-
fail("Got a list of values " + actualValue + " instead of the value " + expectedValue);
119+
fail("Got a list of values " + actualValue + " instead of the expected single value " + expectedValue);
124120
}
125121
actualValue = actualValueList.get(0);
126122
}
127123
else if (actualValue != null && expectedValue != null) {
128-
assertEquals("For JSON path " + this.expression + " type of value",
129-
expectedValue.getClass(), actualValue.getClass());
124+
assertEquals("For JSON path \"" + this.expression + "\", type of value",
125+
expectedValue.getClass().getName(), actualValue.getClass().getName());
130126
}
131-
assertEquals("JSON path " + this.expression, expectedValue, actualValue);
127+
assertEquals("JSON path \"" + this.expression + "\"", expectedValue, actualValue);
128+
}
129+
130+
/**
131+
* Evaluate the JSON path expression against the supplied {@code content}
132+
* and assert that the resulting value is a {@link String}.
133+
* @param content the JSON response content
134+
* @since 4.2.1
135+
*/
136+
public void assertValueIsString(String content) throws ParseException {
137+
Object value = assertExistsAndReturn(content);
138+
String reason = "Expected string at JSON path " + this.expression + " but found " + value;
139+
assertThat(reason, value, instanceOf(String.class));
140+
}
141+
142+
/**
143+
* Evaluate the JSON path expression against the supplied {@code content}
144+
* and assert that the resulting value is a {@link Boolean}.
145+
* @param content the JSON response content
146+
* @since 4.2.1
147+
*/
148+
public void assertValueIsBoolean(String content) throws ParseException {
149+
Object value = assertExistsAndReturn(content);
150+
String reason = "Expected boolean at JSON path " + this.expression + " but found " + value;
151+
assertThat(reason, value, instanceOf(Boolean.class));
152+
}
153+
154+
/**
155+
* Evaluate the JSON path expression against the supplied {@code content}
156+
* and assert that the resulting value is a {@link Number}.
157+
* @param content the JSON response content
158+
* @since 4.2.1
159+
*/
160+
public void assertValueIsNumber(String content) throws ParseException {
161+
Object value = assertExistsAndReturn(content);
162+
String reason = "Expected number at JSON path " + this.expression + " but found " + value;
163+
assertThat(reason, value, instanceOf(Number.class));
132164
}
133165

134166
/**
135-
* Apply the JSON path and assert the resulting value is an array.
167+
* Evaluate the JSON path expression against the supplied {@code content}
168+
* and assert that the resulting value is an array.
169+
* @param content the JSON response content
136170
*/
137-
public void assertValueIsArray(String responseContent) throws ParseException {
138-
Object actualValue = evaluateJsonPath(responseContent);
139-
assertTrue("No value for JSON path \"" + this.expression + "\"", actualValue != null);
140-
String reason = "Expected array at JSON path " + this.expression + " but found " + actualValue;
141-
assertTrue(reason, actualValue instanceof List);
171+
public void assertValueIsArray(String content) throws ParseException {
172+
Object value = assertExistsAndReturn(content);
173+
String reason = "Expected array for JSON path \"" + this.expression + "\" but found " + value;
174+
assertTrue(reason, value instanceof List);
142175
}
143176

144177
/**
145-
* Evaluate the JSON path and assert the resulting content exists.
178+
* Evaluate the JSON path expression against the supplied {@code content}
179+
* and assert that the resulting value is a {@link Map}.
180+
* @param content the JSON response content
181+
* @since 4.2.1
182+
*/
183+
public void assertValueIsMap(String content) throws ParseException {
184+
Object value = assertExistsAndReturn(content);
185+
String reason = "Expected map at JSON path " + this.expression + " but found " + value;
186+
assertThat(reason, value, instanceOf(Map.class));
187+
}
188+
189+
/**
190+
* Evaluate the JSON path expression against the supplied {@code content}
191+
* and assert that the resulting value exists.
192+
* @param content the JSON response content
146193
*/
147194
public void exists(String content) throws ParseException {
148-
Object value = evaluateJsonPath(content);
149-
String reason = "No value for JSON path " + this.expression;
150-
assertTrue(reason, value != null);
151-
if (List.class.isInstance(value)) {
152-
assertTrue(reason, !((List<?>) value).isEmpty());
153-
}
195+
assertExistsAndReturn(content);
154196
}
155197

156198
/**
157-
* Evaluate the JSON path and assert it doesn't point to any content.
199+
* Evaluate the JSON path expression against the supplied {@code content}
200+
* and assert that the resulting value is empty (i.e., that a match for
201+
* the JSON path expression does not exist in the supplied content).
202+
* @param content the JSON response content
158203
*/
159204
public void doesNotExist(String content) throws ParseException {
160205
Object value;
@@ -173,4 +218,30 @@ public void doesNotExist(String content) throws ParseException {
173218
}
174219
}
175220

221+
private Object evaluateJsonPath(String content) throws ParseException {
222+
String message = "No value for JSON path \"" + this.expression + "\", exception: ";
223+
try {
224+
return this.jsonPath.read(content);
225+
}
226+
catch (InvalidPathException ex) {
227+
throw new AssertionError(message + ex.getMessage());
228+
}
229+
catch (ArrayIndexOutOfBoundsException ex) {
230+
throw new AssertionError(message + ex.getMessage());
231+
}
232+
catch (IndexOutOfBoundsException ex) {
233+
throw new AssertionError(message + ex.getMessage());
234+
}
235+
}
236+
237+
private Object assertExistsAndReturn(String content) throws ParseException {
238+
Object value = evaluateJsonPath(content);
239+
String reason = "No value for JSON path \"" + this.expression + "\"";
240+
assertTrue(reason, value != null);
241+
if (List.class.isInstance(value)) {
242+
assertTrue(reason, !((List<?>) value).isEmpty());
243+
}
244+
return value;
245+
}
246+
176247
}

0 commit comments

Comments
 (0)