From d64f2eb03862dcef657fe1a37755184b1ecfda25 Mon Sep 17 00:00:00 2001 From: Bronwyn Perry-Huston Date: Fri, 26 Feb 2016 06:49:37 -0800 Subject: [PATCH] Add JSON matcher to assert on request body Support asserting JSON regardless of order and formatting. Based on same JsonExpectationHelper used in ContentResultMatchers. Issue: SPR-13919 --- .../client/match/ContentRequestMatchers.java | 46 ++++++++++++++++- .../match/ContentRequestMatchersTests.java | 50 ++++++++++++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java b/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java index 2bc9f3ccdc..a6d0baa8b3 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 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. @@ -23,6 +23,7 @@ import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import org.hamcrest.Matcher; +import org.springframework.test.util.JsonExpectationsHelper; import org.w3c.dom.Node; import org.springframework.http.HttpHeaders; @@ -49,6 +50,8 @@ public class ContentRequestMatchers { private final XmlExpectationsHelper xmlHelper; + private final JsonExpectationsHelper jsonHelper; + /** * Class constructor, not for direct instantiation. @@ -56,6 +59,7 @@ public class ContentRequestMatchers { */ protected ContentRequestMatchers() { this.xmlHelper = new XmlExpectationsHelper(); + this.jsonHelper = new JsonExpectationsHelper(); } @@ -194,6 +198,46 @@ public class ContentRequestMatchers { }; } + /** + * Parse the expected and actual strings as JSON and assert the two + * are "similar" - i.e. they contain the same attribute-value pairs + * regardless of formatting with a lenient checking (extensible, and non-strict array + * ordering). + *

Use of this matcher requires the JSONassert library. + * @param expectedJsonContent the expected JSON content + * @since 5.0.5 + */ + public RequestMatcher json(final String expectedJsonContent) { + return json(expectedJsonContent, false); + } + + /** + * Parse the request body and the given string as JSON and assert the two + * are "similar" - i.e. they contain the same attribute-value pairs + * regardless of formatting. + *

Can compare in two modes, depending on {@code strict} parameter value: + *

+ *

Use of this matcher requires the JSONassert library. + * @param expectedJsonContent the expected JSON content + * @param strict enables strict checking + * @since 5.0.5 + */ + public RequestMatcher json(final String expectedJsonContent, final boolean strict) { + return request -> { + try { + MockClientHttpRequest mockRequest = (MockClientHttpRequest) request; + jsonHelper.assertJsonEqual(expectedJsonContent, mockRequest.getBodyAsString(), strict); + } catch (Exception e) { + throw new AssertionError("Failed to parse expected or actual JSON request content", e); + } + }; + } + /** * Abstract base class for XML {@link RequestMatcher}'s. diff --git a/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java b/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java index 262dd2545a..3b760951dd 100644 --- a/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/client/match/ContentRequestMatchersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 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. @@ -142,4 +142,52 @@ public class ContentRequestMatchersTests { MockRestRequestMatchers.content().node(hasXPath("/foo/bar/bar")).match(this.request); } + @Test + public void testJsonLenientMatch() throws Exception { + String content = "{\n \"foo array\":[\"first\",\"second\"] , \"someExtraProperty\": \"which is allowed\" \n}"; + this.request.getBody().write(content.getBytes()); + + MockRestRequestMatchers.content().json("{\n \"foo array\":[\"second\",\"first\"] \n}") + .match(this.request); + MockRestRequestMatchers.content().json("{\n \"foo array\":[\"second\",\"first\"] \n}", false) + .match(this.request); + } + + @Test + public void testJsonStrictMatch() throws Exception { + String content = "{\n \"foo\": \"bar\", \"foo array\":[\"first\",\"second\"] \n}"; + this.request.getBody().write(content.getBytes()); + + MockRestRequestMatchers + .content() + .json("{\n \"foo array\":[\"first\",\"second\"] , \"foo\": \"bar\" \n}", true) + .match(this.request); + } + + @Test(expected = AssertionError.class) + public void testJsonLenientNoMatch() throws Exception { + String content = "{\n \"bar\" : \"foo\" \n}"; + this.request.getBody().write(content.getBytes()); + + MockRestRequestMatchers + .content() + .json("{\n \"foo\" : \"bar\" \n}") + .match(this.request); + MockRestRequestMatchers + .content() + .json("{\n \"foo\" : \"bar\" \n}", false) + .match(this.request); + } + + @Test(expected = AssertionError.class) + public void testJsonStrictNoMatch() throws Exception { + String content = "{\n \"foo array\":[\"first\",\"second\"] , \"someExtraProperty\": \"which is NOT allowed\" \n}"; + this.request.getBody().write(content.getBytes()); + + MockRestRequestMatchers + .content() + .json("{\n \"foo array\":[\"second\",\"first\"] \n}", true) + .match(this.request); + } + }