diff --git a/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java b/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java index cbb74dc6e4..f6b44fdaeb 100644 --- a/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java +++ b/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,9 @@ package org.springframework.test.util; -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; @@ -71,18 +69,33 @@ public class JsonPathExpectationsHelper { * @param matcher the matcher with which to assert the result */ @SuppressWarnings("unchecked") - public void assertValue(String content, Matcher matcher) throws ParseException { + public void assertValue(String content, Matcher matcher) { T value = (T) evaluateJsonPath(content); 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 4.3.3 + */ + @SuppressWarnings("unchecked") + public void assertValue(String content, Matcher matcher, Class targetType) { + T value = (T) evaluateJsonPath(content, targetType); + assertThat("JSON path \"" + this.expression + "\"", value, matcher); + } + /** * 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 content * @param expectedValue the expected value */ - public void assertValue(String content, Object expectedValue) throws ParseException { + public void assertValue(String content, Object expectedValue) { Object actualValue = evaluateJsonPath(content); if ((actualValue instanceof List) && !(expectedValue instanceof List)) { @SuppressWarnings("rawtypes") @@ -96,8 +109,9 @@ public class JsonPathExpectationsHelper { actualValue = actualValueList.get(0); } else if (actualValue != null && expectedValue != null) { - assertEquals("At JSON path \"" + this.expression + "\", type of value", - expectedValue.getClass().getName(), actualValue.getClass().getName()); + if (!actualValue.getClass().equals(expectedValue.getClass())) { + actualValue = evaluateJsonPath(content, expectedValue.getClass()); + } } assertEquals("JSON path \"" + this.expression + "\"", expectedValue, actualValue); } @@ -108,7 +122,7 @@ public class JsonPathExpectationsHelper { * @param content the JSON content * @since 4.2.1 */ - public void assertValueIsString(String content) throws ParseException { + public void assertValueIsString(String content) { Object value = assertExistsAndReturn(content); assertThat(failureReason("a string", value), value, instanceOf(String.class)); } @@ -119,7 +133,7 @@ public class JsonPathExpectationsHelper { * @param content the JSON content * @since 4.2.1 */ - public void assertValueIsBoolean(String content) throws ParseException { + public void assertValueIsBoolean(String content) { Object value = assertExistsAndReturn(content); assertThat(failureReason("a boolean", value), value, instanceOf(Boolean.class)); } @@ -130,7 +144,7 @@ public class JsonPathExpectationsHelper { * @param content the JSON content * @since 4.2.1 */ - public void assertValueIsNumber(String content) throws ParseException { + public void assertValueIsNumber(String content) { Object value = assertExistsAndReturn(content); assertThat(failureReason("a number", value), value, instanceOf(Number.class)); } @@ -140,7 +154,7 @@ public class JsonPathExpectationsHelper { * and assert that the resulting value is an array. * @param content the JSON content */ - public void assertValueIsArray(String content) throws ParseException { + public void assertValueIsArray(String content) { Object value = assertExistsAndReturn(content); assertThat(failureReason("an array", value), value, instanceOf(List.class)); } @@ -151,7 +165,7 @@ public class JsonPathExpectationsHelper { * @param content the JSON content * @since 4.2.1 */ - public void assertValueIsMap(String content) throws ParseException { + public void assertValueIsMap(String content) { Object value = assertExistsAndReturn(content); assertThat(failureReason("a map", value), value, instanceOf(Map.class)); } @@ -164,7 +178,7 @@ public class JsonPathExpectationsHelper { * that the value at the given path is not empty. * @param content the JSON content */ - public void exists(String content) throws ParseException { + public void exists(String content) { assertExistsAndReturn(content); } @@ -176,7 +190,7 @@ public class JsonPathExpectationsHelper { * that the value at the given path is empty. * @param content the JSON content */ - public void doesNotExist(String content) throws ParseException { + public void doesNotExist(String content) { Object value; try { value = evaluateJsonPath(content); @@ -189,7 +203,7 @@ public class JsonPathExpectationsHelper { assertTrue(reason, ((List) value).isEmpty()); } else { - assertTrue(reason, value == null); + assertTrue(reason, (value == null)); } } @@ -200,7 +214,7 @@ public class JsonPathExpectationsHelper { * {@link ObjectUtils#isEmpty(Object)}. * @param content the JSON content */ - public void assertValueIsEmpty(String content) throws ParseException { + public void assertValueIsEmpty(String content) { Object value = evaluateJsonPath(content); assertTrue(failureReason("an empty value", value), ObjectUtils.isEmpty(value)); } @@ -212,33 +226,37 @@ public class JsonPathExpectationsHelper { * {@link ObjectUtils#isEmpty(Object)}. * @param content the JSON content */ - public void assertValueIsNotEmpty(String content) throws ParseException { + public void assertValueIsNotEmpty(String content) { Object value = evaluateJsonPath(content); assertTrue(failureReason("a non-empty value", value), !ObjectUtils.isEmpty(value)); } private String failureReason(String expectedDescription, Object value) { return String.format("Expected %s at JSON path \"%s\" but found: %s", expectedDescription, this.expression, - ObjectUtils.nullSafeToString(StringUtils.quoteIfString(value))); + ObjectUtils.nullSafeToString(StringUtils.quoteIfString(value))); } - private Object evaluateJsonPath(String content) throws ParseException { + private Object evaluateJsonPath(String content) { String message = "No value at 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) { + catch (Throwable ex) { throw new AssertionError(message + ex.getMessage()); } } - private Object assertExistsAndReturn(String content) throws ParseException { + private Object evaluateJsonPath(String content, Class targetType) { + String message = "No value at JSON path \"" + this.expression + "\", exception: "; + try { + return JsonPath.parse(content).read(this.expression, targetType); + } + catch (Throwable ex) { + throw new AssertionError(message + ex.getMessage()); + } + } + + private Object assertExistsAndReturn(String content) { Object value = evaluateJsonPath(content); String reason = "No value at JSON path \"" + this.expression + "\""; assertTrue(reason, value != null); diff --git a/spring-test/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java b/spring-test/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java index 2c3b7a99f9..f308ddc796 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java @@ -69,6 +69,23 @@ 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. + * @since 4.3.3 + */ + public RequestMatcher value(final Matcher matcher, final Class 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 * assert that the result is equal to the supplied value. diff --git a/spring-test/src/test/java/org/springframework/test/util/JsonPathExpectationsHelperTests.java b/spring-test/src/test/java/org/springframework/test/util/JsonPathExpectationsHelperTests.java index ba44e89487..5fa61814cc 100644 --- a/spring-test/src/test/java/org/springframework/test/util/JsonPathExpectationsHelperTests.java +++ b/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"); * 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.rules.ExpectedException; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.core.Is.*; /** * Unit tests for {@link JsonPathExpectationsHelper}. @@ -222,11 +222,14 @@ public class JsonPathExpectationsHelperTests { new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, 5); } - @Test - public void assertValueWithDifferentExpectedType() throws Exception { - exception.expect(AssertionError.class); - exception.expectMessage(equalTo("At JSON path \"$.num\", type of value expected: but was:")); - new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, "5"); + @Test // SPR-14498 + public void assertValueWithNumberConversion() throws Exception { + new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, 5.0); + } + + @Test // SPR-14498 + public void assertValueWithNumberConversionAndMatcher() throws Exception { + new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, is(5.0), Double.class); } @Test