Skip to content

Commit

Permalink
Merge pull request spring-projects#854 from candrews/SPR-13320
Browse files Browse the repository at this point in the history
* SPR-13320:
  Introduce additional JsonPath matchers in Spring MVC Test
  • Loading branch information
sbrannen committed Aug 6, 2015
2 parents 48b965a + fffdd1e commit 35dd307
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,21 @@
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.List;
import java.util.Map;

import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.JsonPath;
import org.hamcrest.Matcher;

import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import static org.hamcrest.MatcherAssert.*;
import static org.springframework.test.util.AssertionErrors.*;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.JsonPath;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.springframework.test.util.AssertionErrors.assertEquals;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.springframework.test.util.AssertionErrors.fail;

/**
* A helper class for applying assertions via JSON path expressions.
Expand All @@ -39,6 +44,8 @@
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Craig Andrews
* @author Sam Brannen
* @since 3.2
*/
public class JsonPathExpectationsHelper {
Expand Down Expand Up @@ -69,92 +76,130 @@ public class JsonPathExpectationsHelper {


/**
* Construct a new JsonPathExpectationsHelper.
* @param expression the JsonPath expression
* @param args arguments to parameterize the JSON path expression with
* Construct a new {@code JsonPathExpectationsHelper}.
* @param expression the {@link JsonPath} expression; never {@code null} or empty
* @param args arguments to parameterize the {@code JsonPath} expression, with
* formatting specifiers defined in {@link String#format(String, Object...)}
*/
public JsonPathExpectationsHelper(String expression, Object... args) {
Assert.hasText(expression, "expression must not be null or empty");
this.expression = String.format(expression, args);
this.jsonPath = (JsonPath) ReflectionUtils.invokeMethod(
compileMethod, null, this.expression, emptyFilters);
}


/**
* Evaluate the JSON path and assert the resulting value with the given {@code Matcher}.
* @param content the response content
* @param matcher the matcher to assert on the resulting json path
* Evaluate the JSON path expression against the supplied {@code content}
* and assert the resulting value with the given {@code Matcher}.
* @param content the JSON response content
* @param matcher the matcher with which to assert the result
*/
@SuppressWarnings("unchecked")
public <T> void assertValue(String content, Matcher<T> matcher) throws ParseException {
T value = (T) evaluateJsonPath(content);
assertThat("JSON path " + this.expression, value, matcher);
}

private Object evaluateJsonPath(String content) throws ParseException {
String message = "No value for JSON path: " + this.expression + ", exception: ";
try {
return this.jsonPath.read(content);
}
catch (InvalidPathException ex) {
throw new AssertionError(message + ex.getMessage());
}
catch (ArrayIndexOutOfBoundsException ex) {
throw new AssertionError(message + ex.getMessage());
}
catch (IndexOutOfBoundsException ex) {
throw new AssertionError(message + ex.getMessage());
}
assertThat("JSON path \"" + this.expression + "\"", value, matcher);
}

/**
* Apply the JSON path and assert the resulting value.
* Evaluate the JSON path expression against the supplied {@code content}
* and assert that the result is equal to the expected value.
* @param content the JSON response content
* @param expectedValue the expected value
*/
public void assertValue(String responseContent, Object expectedValue) throws ParseException {
Object actualValue = evaluateJsonPath(responseContent);
public void assertValue(String content, Object expectedValue) throws ParseException {
Object actualValue = evaluateJsonPath(content);
if ((actualValue instanceof List) && !(expectedValue instanceof List)) {
@SuppressWarnings("rawtypes")
List actualValueList = (List) actualValue;
if (actualValueList.isEmpty()) {
fail("No matching value for JSON path \"" + this.expression + "\"");
}
if (actualValueList.size() != 1) {
fail("Got a list of values " + actualValue + " instead of the value " + expectedValue);
fail("Got a list of values " + actualValue + " instead of the expected single value " + expectedValue);
}
actualValue = actualValueList.get(0);
}
else if (actualValue != null && expectedValue != null) {
assertEquals("For JSON path " + this.expression + " type of value",
expectedValue.getClass(), actualValue.getClass());
assertEquals("For JSON path \"" + this.expression + "\", type of value",
expectedValue.getClass().getName(), actualValue.getClass().getName());
}
assertEquals("JSON path " + this.expression, expectedValue, actualValue);
assertEquals("JSON path \"" + this.expression + "\"", expectedValue, actualValue);
}

/**
* Evaluate the JSON path expression against the supplied {@code content}
* and assert that the resulting value is a {@link String}.
* @param content the JSON response content
* @since 4.2.1
*/
public void assertValueIsString(String content) throws ParseException {
Object value = assertExistsAndReturn(content);
String reason = "Expected string at JSON path " + this.expression + " but found " + value;
assertThat(reason, value, instanceOf(String.class));
}

/**
* Evaluate the JSON path expression against the supplied {@code content}
* and assert that the resulting value is a {@link Boolean}.
* @param content the JSON response content
* @since 4.2.1
*/
public void assertValueIsBoolean(String content) throws ParseException {
Object value = assertExistsAndReturn(content);
String reason = "Expected boolean at JSON path " + this.expression + " but found " + value;
assertThat(reason, value, instanceOf(Boolean.class));
}

/**
* Evaluate the JSON path expression against the supplied {@code content}
* and assert that the resulting value is a {@link Number}.
* @param content the JSON response content
* @since 4.2.1
*/
public void assertValueIsNumber(String content) throws ParseException {
Object value = assertExistsAndReturn(content);
String reason = "Expected number at JSON path " + this.expression + " but found " + value;
assertThat(reason, value, instanceOf(Number.class));
}

/**
* Apply the JSON path and assert the resulting value is an array.
* Evaluate the JSON path expression against the supplied {@code content}
* and assert that the resulting value is an array.
* @param content the JSON response content
*/
public void assertValueIsArray(String responseContent) throws ParseException {
Object actualValue = evaluateJsonPath(responseContent);
assertTrue("No value for JSON path \"" + this.expression + "\"", actualValue != null);
String reason = "Expected array at JSON path " + this.expression + " but found " + actualValue;
assertTrue(reason, actualValue instanceof List);
public void assertValueIsArray(String content) throws ParseException {
Object value = assertExistsAndReturn(content);
String reason = "Expected array for JSON path \"" + this.expression + "\" but found " + value;
assertTrue(reason, value instanceof List);
}

/**
* Evaluate the JSON path and assert the resulting content exists.
* Evaluate the JSON path expression against the supplied {@code content}
* and assert that the resulting value is a {@link Map}.
* @param content the JSON response content
* @since 4.2.1
*/
public void assertValueIsMap(String content) throws ParseException {
Object value = assertExistsAndReturn(content);
String reason = "Expected map at JSON path " + this.expression + " but found " + value;
assertThat(reason, value, instanceOf(Map.class));
}

/**
* Evaluate the JSON path expression against the supplied {@code content}
* and assert that the resulting value exists.
* @param content the JSON response content
*/
public void exists(String content) throws ParseException {
Object value = evaluateJsonPath(content);
String reason = "No value for JSON path " + this.expression;
assertTrue(reason, value != null);
if (List.class.isInstance(value)) {
assertTrue(reason, !((List<?>) value).isEmpty());
}
assertExistsAndReturn(content);
}

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

private Object evaluateJsonPath(String content) throws ParseException {
String message = "No value for JSON path \"" + this.expression + "\", exception: ";
try {
return this.jsonPath.read(content);
}
catch (InvalidPathException ex) {
throw new AssertionError(message + ex.getMessage());
}
catch (ArrayIndexOutOfBoundsException ex) {
throw new AssertionError(message + ex.getMessage());
}
catch (IndexOutOfBoundsException ex) {
throw new AssertionError(message + ex.getMessage());
}
}

private Object assertExistsAndReturn(String content) throws ParseException {
Object value = evaluateJsonPath(content);
String reason = "No value for JSON path \"" + this.expression + "\"";
assertTrue(reason, value != null);
if (List.class.isInstance(value)) {
assertTrue(reason, !((List<?>) value).isEmpty());
}
return value;
}

}
Loading

0 comments on commit 35dd307

Please sign in to comment.