Browse Source

Support target type in JsonPath assertions

This change adds support for a target type in JsonPath assertions in
Spring MVC Test.

The existing assertValue(String expression, Object expectedValue)
transparently falls back on using an alternative JsonPath API that
allows specifying the target type to coerce to.

There is also a new overloaded method
assertValue(String expression, Matcher<T> matcher, Class<T> targetType)
for use with Hamcrest matchers where the target type can be specified.

Issue: SPR-14498
pull/1144/merge
Rossen Stoyanchev 9 years ago
parent
commit
7fdb892042
  1. 38
      spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java
  2. 16
      spring-test/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java
  3. 17
      spring-test/src/test/java/org/springframework/test/util/JsonPathExpectationsHelperTests.java

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

@ -28,9 +28,11 @@ import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsInstanceOf.*; import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.springframework.test.util.AssertionErrors.*; 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. * A helper class for applying assertions via JSON path expressions.
@ -76,6 +78,21 @@ public class JsonPathExpectationsHelper {
assertThat("JSON path \"" + this.expression + "\"", value, matcher); assertThat("JSON path \"" + this.expression + "\"", value, matcher);
} }
/**
* An overloaded variant of {@link #assertValue(String, Matcher)} that also
* accepts a target type for the resulting value. This can be useful for
* matching numbers reliably for example coercing an integer into a double.
* @param content the JSON content
* @param matcher the matcher with which to assert the result
* @param targetType a the expected type of the resulting value
* @since 5.0
*/
@SuppressWarnings("unchecked")
public <T> void assertValue(String content, Matcher<T> matcher, Class<T> targetType) throws ParseException {
T value = (T) evaluateJsonPath(content, targetType);
assertThat("JSON path \"" + this.expression + "\"", value, matcher);
}
/** /**
* Evaluate the JSON path expression against the supplied {@code content} * Evaluate the JSON path expression against the supplied {@code content}
* and assert that the result is equal to the expected value. * and assert that the result is equal to the expected value.
@ -96,8 +113,9 @@ public class JsonPathExpectationsHelper {
actualValue = actualValueList.get(0); actualValue = actualValueList.get(0);
} }
else if (actualValue != null && expectedValue != null) { else if (actualValue != null && expectedValue != null) {
assertEquals("At JSON path \"" + this.expression + "\", type of value", if (!actualValue.getClass().equals(expectedValue.getClass())) {
expectedValue.getClass().getName(), actualValue.getClass().getName()); actualValue = evaluateJsonPath(content, expectedValue.getClass());
}
} }
assertEquals("JSON path \"" + this.expression + "\"", expectedValue, actualValue); assertEquals("JSON path \"" + this.expression + "\"", expectedValue, actualValue);
} }
@ -232,6 +250,16 @@ public class JsonPathExpectationsHelper {
} }
} }
private Object evaluateJsonPath(String content, Class<?> targetType) throws ParseException {
String message = "No value at JSON path \"" + this.expression + "\", exception: ";
try {
return JsonPath.parse(content).read(this.expression, targetType);
}
catch (InvalidPathException | IndexOutOfBoundsException ex) {
throw new AssertionError(message + ex.getMessage());
}
}
private Object assertExistsAndReturn(String content) throws ParseException { private Object assertExistsAndReturn(String content) throws ParseException {
Object value = evaluateJsonPath(content); Object value = evaluateJsonPath(content);
String reason = "No value at JSON path \"" + this.expression + "\""; String reason = "No value at JSON path \"" + this.expression + "\"";

16
spring-test/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java

@ -69,6 +69,22 @@ public class JsonPathRequestMatchers {
}; };
} }
/**
* An overloaded variant of (@link {@link #value(Matcher)} that also
* accepts a target type for the resulting value that the matcher can work
* reliably against. This can be useful for matching numbers reliably for
* example coercing an integer into a double.
*/
public <T> RequestMatcher value(final Matcher<T> matcher, final Class<T> targetType) {
return new AbstractJsonPathRequestMatcher() {
@Override
protected void matchInternal(MockClientHttpRequest request) throws IOException, ParseException {
String body = request.getBodyAsString();
JsonPathRequestMatchers.this.jsonPathHelper.assertValue(body, matcher, targetType);
}
};
}
/** /**
* Evaluate the JSON path expression against the request content and * Evaluate the JSON path expression against the request content and
* assert that the result is equal to the supplied value. * assert that the result is equal to the supplied value.

17
spring-test/src/test/java/org/springframework/test/util/JsonPathExpectationsHelperTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2004-2015 the original author or authors. * Copyright 2004-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.core.Is.is;
/** /**
* Unit tests for {@link JsonPathExpectationsHelper}. * Unit tests for {@link JsonPathExpectationsHelper}.
@ -222,11 +222,14 @@ public class JsonPathExpectationsHelperTests {
new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, 5); new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, 5);
} }
@Test @Test // SPR-14498
public void assertValueWithDifferentExpectedType() throws Exception { public void assertValueWithNumberConversion() throws Exception {
exception.expect(AssertionError.class); new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, 5.0);
exception.expectMessage(equalTo("At JSON path \"$.num\", type of value expected:<java.lang.String> but was:<java.lang.Integer>")); }
new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, "5");
@Test // SPR-14498
public void assertValueWithNumberConversionAndMatcher() throws Exception {
new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, is(5.0), Double.class);
} }
@Test @Test

Loading…
Cancel
Save