Differentiate b/t (in)definite results in JsonPath assertions
Prior to this commit, the exists() method in JsonPathExpectationsHelper correctly asserted that the evaluated JsonPath expression resulted in a value (i.e., that a non-null value exists); however, if the value was an empty array, the exists() method always threw an AssertionError. The existing behavior makes sense if the JsonPath expression is 'indefinite' -- for example, if the expression uses a filter to select results based on a predicate for which there is no match in the JSON document, but the existing behavior is illogical and therefore invalid if the JsonPath expression is 'definite' (i.e., directly references an array in the JSON document that exists but happens to be empty). For example, prior to this commit, the following threw an AssertionError. new JsonPathExpectationsHelper("$.arr").exists("{ 'arr': [] }"); Similar arguments can be made for the doesNotExist() method. After thorough analysis of the status quo, it has become apparent that the existing specialized treatment of arrays is a result of the fact that the JsonPath library always returns an empty list if the path is an 'indefinite' path that does not evaluate to a specific result. Consult the discussion on "What is Returned When?" in the JsonPath documentation for details: https://github.com/jayway/JsonPath#what-is-returned-when This commit addresses these issues by ensuring that empty arrays are considered existent if the JsonPath expression is definite but nonexistent if the expression is indefinite. Issue: SPR-13351
This commit is contained in:
parent
d2503340e7
commit
07bb0378b9
|
@ -171,7 +171,7 @@ public class JsonPathExpectationsHelper {
|
|||
public void assertValueIsArray(String content) throws ParseException {
|
||||
Object value = assertExistsAndReturn(content);
|
||||
String reason = "Expected an array at JSON path \"" + this.expression + "\" but found: " + value;
|
||||
assertTrue(reason, value instanceof List);
|
||||
assertThat(reason, value, instanceOf(List.class));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -188,7 +188,10 @@ public class JsonPathExpectationsHelper {
|
|||
|
||||
/**
|
||||
* Evaluate the JSON path expression against the supplied {@code content}
|
||||
* and assert that the resulting value exists.
|
||||
* and assert that a non-null value exists at the given path.
|
||||
* <p>If the JSON path expression is not
|
||||
* {@linkplain JsonPath#isDefinite() definite}, this method asserts
|
||||
* that the value at the given path is not <em>empty</em>.
|
||||
* @param content the JSON content
|
||||
*/
|
||||
public void exists(String content) throws ParseException {
|
||||
|
@ -197,8 +200,10 @@ public class JsonPathExpectationsHelper {
|
|||
|
||||
/**
|
||||
* 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).
|
||||
* and assert that a value does not exist at the given path.
|
||||
* <p>If the JSON path expression is not
|
||||
* {@linkplain JsonPath#isDefinite() definite}, this method asserts
|
||||
* that the value at the given path is <em>empty</em>.
|
||||
* @param content the JSON content
|
||||
*/
|
||||
public void doesNotExist(String content) throws ParseException {
|
||||
|
@ -210,7 +215,7 @@ public class JsonPathExpectationsHelper {
|
|||
return;
|
||||
}
|
||||
String reason = "Expected no value at JSON path \"" + this.expression + "\" but found: " + value;
|
||||
if (List.class.isInstance(value)) {
|
||||
if (pathIsIndefinite() && value instanceof List) {
|
||||
assertTrue(reason, ((List<?>) value).isEmpty());
|
||||
}
|
||||
else {
|
||||
|
@ -238,7 +243,14 @@ public class JsonPathExpectationsHelper {
|
|||
Object value = evaluateJsonPath(content);
|
||||
String reason = "No value at JSON path \"" + this.expression + "\"";
|
||||
assertTrue(reason, value != null);
|
||||
if (pathIsIndefinite() && value instanceof List) {
|
||||
assertTrue(reason, !((List<?>) value).isEmpty());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private boolean pathIsIndefinite() {
|
||||
return !this.jsonPath.isDefinite();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -78,8 +78,10 @@ public class JsonPathResultMatchers {
|
|||
|
||||
/**
|
||||
* Evaluate the JSON path expression against the response content and
|
||||
* assert that the result is not empty (i.e., that a match for the JSON
|
||||
* path expression exists in the response content).
|
||||
* assert that a non-null value exists at the given path.
|
||||
* <p>If the JSON path expression is not
|
||||
* {@linkplain com.jayway.jsonpath.JsonPath#isDefinite definite},
|
||||
* this method asserts that the value at the given path is not <em>empty</em>.
|
||||
*/
|
||||
public ResultMatcher exists() {
|
||||
return new ResultMatcher() {
|
||||
|
@ -93,8 +95,10 @@ public class JsonPathResultMatchers {
|
|||
|
||||
/**
|
||||
* Evaluate the JSON path expression against the response content and
|
||||
* assert that the result is empty (i.e., that a match for the JSON
|
||||
* path expression does not exist in the response content).
|
||||
* assert that a value does not exist at the given path.
|
||||
* <p>If the JSON path expression is not
|
||||
* {@linkplain com.jayway.jsonpath.JsonPath#isDefinite definite}, this
|
||||
* method asserts that the value at the given path is <em>empty</em>.
|
||||
*/
|
||||
public ResultMatcher doesNotExist() {
|
||||
return new ResultMatcher() {
|
||||
|
|
|
@ -31,16 +31,23 @@ import static org.hamcrest.CoreMatchers.*;
|
|||
*/
|
||||
public class JsonPathExpectationsHelperTests {
|
||||
|
||||
private static final String CONTENT = "{" + //
|
||||
"\"str\": \"foo\"," + //
|
||||
"\"nr\": 5," + //
|
||||
"\"bool\": true," + //
|
||||
"\"arr\": [\"bar\"]," + //
|
||||
"\"emptyArray\": []," + //
|
||||
"\"colorMap\": {\"red\": \"rojo\"}," + //
|
||||
"\"emptyMap\": {}," + //
|
||||
private static final String CONTENT = "{" + //
|
||||
"\"str\": \"foo\", " + //
|
||||
"\"num\": 5, " + //
|
||||
"\"bool\": true, " + //
|
||||
"\"arr\": [\"bar\"], " + //
|
||||
"\"emptyArray\": [], " + //
|
||||
"\"colorMap\": {\"red\": \"rojo\"}, " + //
|
||||
"\"emptyMap\": {} " + //
|
||||
"}";
|
||||
|
||||
private static final String SIMPSONS = "{ \"familyMembers\": [ " + //
|
||||
"{\"name\": \"Homer\" }, " + //
|
||||
"{\"name\": \"Marge\" }, " + //
|
||||
"{\"name\": \"Bart\" }, " + //
|
||||
"{\"name\": \"Lisa\" }, " + //
|
||||
"{\"name\": \"Maggie\"} " + //
|
||||
" ] }";
|
||||
|
||||
@Rule
|
||||
public final ExpectedException exception = ExpectedException.none();
|
||||
|
@ -51,21 +58,75 @@ public class JsonPathExpectationsHelperTests {
|
|||
new JsonPathExpectationsHelper("$.str").exists(CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existsForAnEmptyArray() throws Exception {
|
||||
new JsonPathExpectationsHelper("$.emptyArray").exists(CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existsForAnEmptyMap() throws Exception {
|
||||
new JsonPathExpectationsHelper("$.emptyMap").exists(CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existsForIndefinatePathWithResults() throws Exception {
|
||||
new JsonPathExpectationsHelper("$.familyMembers[?(@.name == 'Bart')]").exists(SIMPSONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existsForIndefinatePathWithEmptyResults() throws Exception {
|
||||
String expression = "$.familyMembers[?(@.name == 'Dilbert')]";
|
||||
exception.expect(AssertionError.class);
|
||||
exception.expectMessage("No value at JSON path \"" + expression + "\"");
|
||||
new JsonPathExpectationsHelper(expression).exists(SIMPSONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotExist() throws Exception {
|
||||
new JsonPathExpectationsHelper("$.bogus").doesNotExist(CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotExistForAnEmptyArray() throws Exception {
|
||||
String expression = "$.emptyArray";
|
||||
exception.expect(AssertionError.class);
|
||||
exception.expectMessage("Expected no value at JSON path \"" + expression + "\" but found: []");
|
||||
new JsonPathExpectationsHelper(expression).doesNotExist(CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotExistForAnEmptyMap() throws Exception {
|
||||
String expression = "$.emptyMap";
|
||||
exception.expect(AssertionError.class);
|
||||
exception.expectMessage("Expected no value at JSON path \"" + expression + "\" but found: {}");
|
||||
new JsonPathExpectationsHelper(expression).doesNotExist(CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotExistForIndefinatePathWithResults() throws Exception {
|
||||
String expression = "$.familyMembers[?(@.name == 'Bart')]";
|
||||
exception.expect(AssertionError.class);
|
||||
exception.expectMessage("Expected no value at JSON path \"" + expression
|
||||
+ "\" but found: [{\"name\":\"Bart\"}]");
|
||||
new JsonPathExpectationsHelper(expression).doesNotExist(SIMPSONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotExistForIndefinatePathWithEmptyResults() throws Exception {
|
||||
String expression = "$.familyMembers[?(@.name == 'Dilbert')]";
|
||||
new JsonPathExpectationsHelper(expression).doesNotExist(SIMPSONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertValue() throws Exception {
|
||||
new JsonPathExpectationsHelper("$.nr").assertValue(CONTENT, 5);
|
||||
new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, 5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertValueWithDifferentExpectedType() throws Exception {
|
||||
exception.expect(AssertionError.class);
|
||||
exception.expectMessage(equalTo("At JSON path \"$.nr\", type of value expected:<java.lang.String> but was:<java.lang.Integer>"));
|
||||
new JsonPathExpectationsHelper("$.nr").assertValue(CONTENT, "5");
|
||||
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
|
||||
|
@ -81,7 +142,7 @@ public class JsonPathExpectationsHelperTests {
|
|||
|
||||
@Test
|
||||
public void assertValueIsNumber() throws Exception {
|
||||
new JsonPathExpectationsHelper("$.nr").assertValueIsNumber(CONTENT);
|
||||
new JsonPathExpectationsHelper("$.num").assertValueIsNumber(CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -98,7 +159,7 @@ public class JsonPathExpectationsHelperTests {
|
|||
@Test
|
||||
public void assertValueIsBooleanForNonBoolean() throws Exception {
|
||||
exception.expect(AssertionError.class);
|
||||
new JsonPathExpectationsHelper("$.nr").assertValueIsBoolean(CONTENT);
|
||||
new JsonPathExpectationsHelper("$.num").assertValueIsBoolean(CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -74,6 +74,16 @@ public class JsonPathResultMatchersTests {
|
|||
new JsonPathResultMatchers("$.foo").exists().match(stubMvcResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existsForAnEmptyArray() throws Exception {
|
||||
new JsonPathResultMatchers("$.emptyArray").exists().match(stubMvcResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existsForAnEmptyMap() throws Exception {
|
||||
new JsonPathResultMatchers("$.emptyMap").exists().match(stubMvcResult);
|
||||
}
|
||||
|
||||
@Test(expected = AssertionError.class)
|
||||
public void existsNoMatch() throws Exception {
|
||||
new JsonPathResultMatchers("$.bogus").exists().match(stubMvcResult);
|
||||
|
@ -89,6 +99,16 @@ public class JsonPathResultMatchersTests {
|
|||
new JsonPathResultMatchers("$.foo").doesNotExist().match(stubMvcResult);
|
||||
}
|
||||
|
||||
@Test(expected = AssertionError.class)
|
||||
public void doesNotExistForAnEmptyArray() throws Exception {
|
||||
new JsonPathResultMatchers("$.emptyArray").doesNotExist().match(stubMvcResult);
|
||||
}
|
||||
|
||||
@Test(expected = AssertionError.class)
|
||||
public void doesNotExistForAnEmptyMap() throws Exception {
|
||||
new JsonPathResultMatchers("$.emptyMap").doesNotExist().match(stubMvcResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isArray() throws Exception {
|
||||
new JsonPathResultMatchers("$.qux").isArray().match(stubMvcResult);
|
||||
|
|
Loading…
Reference in New Issue