Move response body directly in AbstractMockHttpServletResponseAssert

This commit removes ResponseBodyAssert and rather offers first-class
access support for the response body at the root level using bodyText(),
bodyJson(), and body().

This avoids a double navigation to assert the response body.

See gh-32712
This commit is contained in:
Stéphane Nicoll
2024-05-06 15:11:30 +02:00
parent 24cc77655f
commit 5567d14700
10 changed files with 195 additions and 301 deletions

View File

@@ -19,7 +19,6 @@ package org.springframework.test.json;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
@@ -146,7 +145,6 @@ class AbstractJsonContentAssertTests {
assertThat(forJson(NULLS)).doesNotHavePath("$.missing");
}
@Test
void doesNotHavePathForPresent() {
String expression = "$.valuename";
@@ -154,7 +152,6 @@ class AbstractJsonContentAssertTests {
.isThrownBy(() -> assertThat(forJson(NULLS)).doesNotHavePath(expression))
.satisfies(hasFailedToNotMatchPath(expression));
}
}
@Nested
@@ -330,13 +327,12 @@ class AbstractJsonContentAssertTests {
private record Customer(long id, String username) {}
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json) {
return () -> new TestJsonContentAssert(json, null, null, null);
return () -> new TestJsonContentAssert(json, null);
}
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json, GenericHttpMessageConverter<Object> jsonHttpMessageConverter) {
return () -> new TestJsonContentAssert(json, jsonHttpMessageConverter, null, null);
return () -> new TestJsonContentAssert(json, jsonHttpMessageConverter);
}
}
@Nested
@@ -548,7 +544,6 @@ class AbstractJsonContentAssertTests {
.isThrownBy(() -> assertThat(forJson(SOURCE)).isStrictlyEqualTo(expected));
}
@Test
void isNotEqualToWhenStringIsMatchingShouldFail() {
assertThatExceptionOfType(AssertionError.class)
@@ -720,10 +715,19 @@ class AbstractJsonContentAssertTests {
assertThat(forJson(SOURCE)).isNotStrictlyEqualTo(expected);
}
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json) {
return () -> new TestJsonContentAssert(json, null, getClass(), null);
@Test
void withResourceLoadClassShouldAllowToLoadRelativeContent() {
AbstractJsonContentAssert<?> jsonAssert = assertThat(forJson(NULLS)).withResourceLoadClass(String.class);
assertThatIllegalStateException()
.isThrownBy(() -> jsonAssert.isLenientlyEqualTo("nulls.json"))
.withMessage("Unable to load JSON from class path resource [java/lang/nulls.json]");
assertThat(forJson(NULLS)).withResourceLoadClass(JsonContent.class).isLenientlyEqualTo("nulls.json");
}
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json) {
return () -> new TestJsonContentAssert(json, null).withResourceLoadClass(getClass());
}
}
@@ -768,13 +772,13 @@ class AbstractJsonContentAssertTests {
}
private AssertProvider<AbstractJsonContentAssert<?>> forJson(@Nullable String json) {
return () -> new TestJsonContentAssert(json, null, null, null);
return () -> new TestJsonContentAssert(json, null);
}
private static class TestJsonContentAssert extends AbstractJsonContentAssert<TestJsonContentAssert> {
public TestJsonContentAssert(@Nullable String json, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter, @Nullable Class<?> resourceLoadClass, @Nullable Charset charset) {
super(json, jsonMessageConverter, resourceLoadClass, charset, TestJsonContentAssert.class);
public TestJsonContentAssert(@Nullable String json, @Nullable GenericHttpMessageConverter<Object> jsonMessageConverter) {
super(json, jsonMessageConverter, TestJsonContentAssert.class);
}
}

View File

@@ -16,15 +16,17 @@
package org.springframework.test.web.servlet.assertj;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import org.assertj.core.api.AssertProvider;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.json.JsonContent;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
@@ -34,12 +36,50 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*/
public class AbstractMockHttpServletResponseAssertTests {
@Test
void bodyText() {
MockHttpServletResponse response = createResponse("OK");
assertThat(fromResponse(response)).bodyText().isEqualTo("OK");
}
@Test
void bodyJsonWithJsonPath() {
MockHttpServletResponse response = createResponse("{\"albumById\": {\"name\": \"Greatest hits\"}}");
assertThat(fromResponse(response)).bodyJson()
.extractingPath("$.albumById.name").isEqualTo("Greatest hits");
}
@Test
void bodyJsonCanLoadResourceRelativeToClass() {
MockHttpServletResponse response = createResponse("{ \"name\" : \"Spring\", \"age\" : 123 }");
// See org/springframework/test/json/example.json
assertThat(fromResponse(response)).bodyJson().withResourceLoadClass(JsonContent.class)
.isLenientlyEqualTo("example.json");
}
@Test
void bodyWithByteArray() throws UnsupportedEncodingException {
byte[] bytes = "OK".getBytes(StandardCharsets.UTF_8);
MockHttpServletResponse response = new MockHttpServletResponse();
response.getWriter().write("OK");
response.setContentType(StandardCharsets.UTF_8.name());
assertThat(fromResponse(response)).body().isEqualTo(bytes);
}
@Test
void hasBodyTextEqualTo() throws UnsupportedEncodingException {
MockHttpServletResponse response = new MockHttpServletResponse();
response.getWriter().write("OK");
response.setContentType(StandardCharsets.UTF_8.name());
assertThat(fromResponse(response)).hasBodyTextEqualTo("OK");
}
@Test
void hasForwardedUrl() {
String forwardedUrl = "https://example.com/42";
MockHttpServletResponse response = new MockHttpServletResponse();
response.setForwardedUrl(forwardedUrl);
assertThat(response).hasForwardedUrl(forwardedUrl);
assertThat(fromResponse(response)).hasForwardedUrl(forwardedUrl);
}
@Test
@@ -48,7 +88,7 @@ public class AbstractMockHttpServletResponseAssertTests {
MockHttpServletResponse response = new MockHttpServletResponse();
response.setForwardedUrl(forwardedUrl);
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(response).hasForwardedUrl("another"))
.isThrownBy(() -> assertThat(fromResponse(response)).hasForwardedUrl("another"))
.withMessageContainingAll("Forwarded URL", forwardedUrl, "another");
}
@@ -57,7 +97,7 @@ public class AbstractMockHttpServletResponseAssertTests {
String redirectedUrl = "https://example.com/42";
MockHttpServletResponse response = new MockHttpServletResponse();
response.addHeader(HttpHeaders.LOCATION, redirectedUrl);
assertThat(response).hasRedirectedUrl(redirectedUrl);
assertThat(fromResponse(response)).hasRedirectedUrl(redirectedUrl);
}
@Test
@@ -66,29 +106,25 @@ public class AbstractMockHttpServletResponseAssertTests {
MockHttpServletResponse response = new MockHttpServletResponse();
response.addHeader(HttpHeaders.LOCATION, redirectedUrl);
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(response).hasRedirectedUrl("another"))
.isThrownBy(() -> assertThat(fromResponse(response)).hasRedirectedUrl("another"))
.withMessageContainingAll("Redirected URL", redirectedUrl, "another");
}
@Test
void bodyHasContent() throws UnsupportedEncodingException {
MockHttpServletResponse response = new MockHttpServletResponse();
response.getWriter().write("OK");
assertThat(response).body().asString().isEqualTo("OK");
private MockHttpServletResponse createResponse(String body) {
try {
MockHttpServletResponse response = new MockHttpServletResponse();
response.setContentType(StandardCharsets.UTF_8.name());
response.getWriter().write(body);
return response;
}
catch (UnsupportedEncodingException ex) {
throw new IllegalStateException(ex);
}
}
@Test
void bodyHasContentWithResponseCharacterEncoding() throws UnsupportedEncodingException {
byte[] bytes = "OK".getBytes(StandardCharsets.UTF_8);
MockHttpServletResponse response = new MockHttpServletResponse();
response.getWriter().write("OK");
response.setContentType(StandardCharsets.UTF_8.name());
assertThat(response).body().isEqualTo(bytes);
}
private static ResponseAssert assertThat(MockHttpServletResponse response) {
return new ResponseAssert(response);
private static AssertProvider<ResponseAssert> fromResponse(MockHttpServletResponse response) {
return () -> new ResponseAssert(response);
}

View File

@@ -250,19 +250,19 @@ public class AssertableMockMvcIntegrationTests {
@Test
void jsonPathContent() {
assertThat(perform(get("/message"))).body().jsonPath()
assertThat(perform(get("/message"))).bodyJson()
.extractingPath("$.message").asString().isEqualTo("hello");
}
@Test
void jsonContentCanLoadResourceFromClasspath() {
assertThat(perform(get("/message"))).body().json().isLenientlyEqualTo(
assertThat(perform(get("/message"))).bodyJson().isLenientlyEqualTo(
new ClassPathResource("message.json", AssertableMockMvcIntegrationTests.class));
}
@Test
void jsonContentUsingResourceLoaderClass() {
assertThat(perform(get("/message"))).body().json(AssertableMockMvcIntegrationTests.class)
assertThat(perform(get("/message"))).bodyJson().withResourceLoadClass(AssertableMockMvcIntegrationTests.class)
.isLenientlyEqualTo("message.json");
}

View File

@@ -67,26 +67,26 @@ class AssertableMockMvcTests {
void createWithExistingWebApplicationContext() {
try (GenericWebApplicationContext wac = create(WebConfiguration.class)) {
AssertableMockMvc mockMvc = AssertableMockMvc.from(wac);
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 41");
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 42");
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 41");
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 42");
}
}
@Test
void createWithControllerClassShouldInstantiateControllers() {
AssertableMockMvc mockMvc = AssertableMockMvc.of(HelloController.class, CounterController.class);
assertThat(mockMvc.perform(get("/hello"))).body().isEqualTo("Hello World");
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 1");
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 2");
assertThat(mockMvc.perform(get("/hello"))).hasBodyTextEqualTo("Hello World");
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 1");
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 2");
}
@Test
void createWithControllersShouldUseThemAsIs() {
AssertableMockMvc mockMvc = AssertableMockMvc.of(new HelloController(),
new CounterController(new AtomicInteger(41)));
assertThat(mockMvc.perform(get("/hello"))).body().isEqualTo("Hello World");
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 42");
assertThat(mockMvc.perform(post("/increase"))).body().isEqualTo("counter 43");
assertThat(mockMvc.perform(get("/hello"))).hasBodyTextEqualTo("Hello World");
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 42");
assertThat(mockMvc.perform(post("/increase"))).hasBodyTextEqualTo("counter 43");
}
@Test
@@ -99,7 +99,7 @@ class AssertableMockMvcTests {
@Test
void createWithControllersHasNoHttpMessageConverter() {
AssertableMockMvc mockMvc = AssertableMockMvc.of(new HelloController());
AbstractJsonContentAssert<?> jsonContentAssert = assertThat(mockMvc.perform(get("/json"))).hasStatusOk().body().jsonPath();
AbstractJsonContentAssert<?> jsonContentAssert = assertThat(mockMvc.perform(get("/json"))).hasStatusOk().bodyJson();
assertThatIllegalStateException()
.isThrownBy(() -> jsonContentAssert.extractingPath("$").convertTo(Message.class))
.withMessageContaining("No JSON message converter available");
@@ -109,7 +109,7 @@ class AssertableMockMvcTests {
void createWithControllerCanConfigureHttpMessageConverters() {
AssertableMockMvc mockMvc = AssertableMockMvc.of(HelloController.class)
.withHttpMessageConverters(List.of(jsonHttpMessageConverter));
assertThat(mockMvc.perform(get("/json"))).hasStatusOk().body().jsonPath()
assertThat(mockMvc.perform(get("/json"))).hasStatusOk().bodyJson()
.extractingPath("$").convertTo(Message.class).satisfies(message -> {
assertThat(message.message()).isEqualTo("Hello World");
assertThat(message.counter()).isEqualTo(42);
@@ -122,7 +122,7 @@ class AssertableMockMvcTests {
MappingJackson2HttpMessageConverter converter = spy(jsonHttpMessageConverter);
AssertableMockMvc mockMvc = AssertableMockMvc.of(HelloController.class)
.withHttpMessageConverters(List.of(mock(), mock(), converter));
assertThat(mockMvc.perform(get("/json"))).hasStatusOk().body().jsonPath()
assertThat(mockMvc.perform(get("/json"))).hasStatusOk().bodyJson()
.extractingPath("$").convertTo(Message.class).satisfies(message -> {
assertThat(message.message()).isEqualTo("Hello World");
assertThat(message.counter()).isEqualTo(42);

View File

@@ -1,88 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.web.servlet.assertj;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.assertj.core.api.AssertProvider;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.json.JsonContent;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ResponseBodyAssert}.
*
* @author Brian Clozel
* @author Stephane Nicoll
*/
class ResponseBodyAssertTests {
@Test
void isEqualToWithByteArray() {
MockHttpServletResponse response = createResponse("hello");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
assertThat(fromResponse(response)).isEqualTo("hello".getBytes(StandardCharsets.UTF_8));
}
@Test
void isEqualToWithString() {
MockHttpServletResponse response = createResponse("hello");
assertThat(fromResponse(response)).isEqualTo("hello");
}
@Test
void jsonPathWithJsonResponseShouldPass() {
MockHttpServletResponse response = createResponse("{\"message\": \"hello\"}");
assertThat(fromResponse(response)).jsonPath().extractingPath("$.message").isEqualTo("hello");
}
@Test
void jsonPathWithJsonCompatibleResponseShouldPass() {
MockHttpServletResponse response = createResponse("{\"albumById\": {\"name\": \"Greatest hits\"}}");
assertThat(fromResponse(response)).jsonPath()
.extractingPath("$.albumById.name").isEqualTo("Greatest hits");
}
@Test
void jsonCanLoadResourceRelativeToClass() {
MockHttpServletResponse response = createResponse("{ \"name\" : \"Spring\", \"age\" : 123 }");
// See org/springframework/test/json/example.json
assertThat(fromResponse(response)).json(JsonContent.class).isLenientlyEqualTo("example.json");
}
private MockHttpServletResponse createResponse(String body) {
try {
MockHttpServletResponse response = new MockHttpServletResponse();
response.getWriter().print(body);
return response;
}
catch (UnsupportedEncodingException ex) {
throw new IllegalStateException(ex);
}
}
private AssertProvider<ResponseBodyAssert> fromResponse(MockHttpServletResponse response) {
return () -> new ResponseBodyAssert(response.getContentAsByteArray(), Charset.forName(response.getCharacterEncoding()), null);
}
}