From 7fdb892042750be534c4a677b2bdc01d7ede8bd8 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 26 Aug 2016 10:33:41 -0400 Subject: [PATCH] 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 matcher, Class targetType) for use with Hamcrest matchers where the target type can be specified. Issue: SPR-14498 --- .../test/util/JsonPathExpectationsHelper.java | 38 ++++++++++++++++--- .../client/match/JsonPathRequestMatchers.java | 16 ++++++++ .../util/JsonPathExpectationsHelperTests.java | 17 +++++---- 3 files changed, 59 insertions(+), 12 deletions(-) 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 e1d9d1615c6..42c128fe3d5 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 @@ -28,9 +28,11 @@ import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.core.IsInstanceOf.*; -import static org.springframework.test.util.AssertionErrors.*; +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. @@ -76,6 +78,21 @@ public class JsonPathExpectationsHelper { 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 void assertValue(String content, Matcher matcher, Class 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} * and assert that the result is equal to the expected value. @@ -96,8 +113,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); } @@ -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 { Object value = evaluateJsonPath(content); String reason = "No value at JSON path \"" + this.expression + "\""; 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 2c3b7a99f94..1acb2c38e1a 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,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 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 ba44e894870..e3b8d532fc1 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.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