Add support for fluent preparation of the request in MockMvcTester

See gh-32913
This commit is contained in:
Stéphane Nicoll 2024-05-28 11:08:13 +02:00
parent a3e7fd47c6
commit 8d2bc3bdba
3 changed files with 306 additions and 78 deletions

View File

@ -23,13 +23,18 @@ import java.util.Map;
import java.util.function.Function;
import java.util.stream.StreamSupport;
import org.assertj.core.api.AssertProvider;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.AbstractMockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
@ -58,11 +63,25 @@ import org.springframework.web.context.WebApplicationContext;
* MockMvcTester mvc = MockMvcTester.of(new PersonController());
* </code></pre>
*
* <p>Once a tester instance is available, you can perform requests in a similar
* fashion as with {@link MockMvc}, and wrapping the result in
* {@code assertThat()} provides access to assertions. For instance:
* <p>Simple, single-statement assertions can be done wrapping the request
* builder in {@code assertThat()} provides access to assertions. For instance:
* <pre><code class="java">
* // perform a GET on /hi and assert the response body is equal to Hello
* assertThat(mvc.get().uri("/hi")).hasStatusOk().hasBodyTextEqualTo("Hello");
* </code></pre>
*
*<p>For more complex scenarios the {@linkplain MvcTestResult result} of the
* exchange can be assigned in a variable to run multiple assertions:
* <pre><code class="java">
* // perform a POST on /save and assert the response body is empty
* MvcTestResult result = mvc.post().uri("/save").exchange();
* assertThat(result).hasStatus(HttpStatus.CREATED);
* assertThat(result).body().isEmpty();
* </code></pre>
*
* <p>You can also perform requests using the static builders approach that
* {@link MockMvc} uses. For instance:<pre><code class="java">
* // perform a GET on /hi and assert the response body is equal to Hello
* assertThat(mvc.perform(get("/hi")))
* .hasStatusOk().hasBodyTextEqualTo("Hello");
* </code></pre>
@ -74,12 +93,11 @@ import org.springframework.web.context.WebApplicationContext;
* which allows you to assert that a request failed unexpectedly:
* <pre><code class="java">
* // perform a GET on /boom and assert the message for the the unresolved exception
* assertThat(mvc.perform(get("/boom")))
* .hasUnresolvedException())
* assertThat(mvc.get().uri("/boom")).hasUnresolvedException())
* .withMessage("Test exception");
* </code></pre>
*
* <p>{@link MockMvcTester} can be configured with a list of
* <p>{@code MockMvcTester} can be configured with a list of
* {@linkplain HttpMessageConverter message converters} to allow the response
* body to be deserialized, rather than asserting on the raw values.
*
@ -104,8 +122,7 @@ public final class MockMvcTester {
}
/**
* Create a {@link MockMvcTester} instance that delegates to the given
* {@link MockMvc} instance.
* Create an instance that delegates to the given {@link MockMvc} instance.
* @param mockMvc the MockMvc instance to delegate calls to
*/
public static MockMvcTester create(MockMvc mockMvc) {
@ -113,9 +130,9 @@ public final class MockMvcTester {
}
/**
* Create an {@link MockMvcTester} instance using the given, fully
* initialized (i.e., <em>refreshed</em>) {@link WebApplicationContext}. The
* given {@code customizations} are applied to the {@link DefaultMockMvcBuilder}
* Create an instance using the given, fully initialized (i.e.,
* <em>refreshed</em>) {@link WebApplicationContext}. The given
* {@code customizations} are applied to the {@link DefaultMockMvcBuilder}
* that ultimately creates the underlying {@link MockMvc} instance.
* <p>If no further customization of the underlying {@link MockMvc} instance
* is required, use {@link #from(WebApplicationContext)}.
@ -134,8 +151,8 @@ public final class MockMvcTester {
}
/**
* Shortcut to create an {@link MockMvcTester} instance using the given,
* fully initialized (i.e., <em>refreshed</em>) {@link WebApplicationContext}.
* Shortcut to create an instance using the given fully initialized (i.e.,
* <em>refreshed</em>) {@link WebApplicationContext}.
* <p>Consider using {@link #from(WebApplicationContext, Function)} if
* further customization of the underlying {@link MockMvc} instance is
* required.
@ -148,9 +165,8 @@ public final class MockMvcTester {
}
/**
* Create an {@link MockMvcTester} instance by registering one or more
* {@code @Controller} instances and configuring Spring MVC infrastructure
* programmatically.
* Create an instance by registering one or more {@code @Controller} instances
* and configuring Spring MVC infrastructure programmatically.
* <p>This allows full control over the instantiation and initialization of
* controllers and their dependencies, similar to plain unit tests while
* also making it possible to test one controller at a time.
@ -170,8 +186,8 @@ public final class MockMvcTester {
}
/**
* Shortcut to create an {@link MockMvcTester} instance by registering one
* or more {@code @Controller} instances.
* Shortcut to create an instance by registering one or more {@code @Controller}
* instances.
* <p>The minimum infrastructure required by the
* {@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet}
* to serve requests with annotated controllers is created. Consider using
@ -187,8 +203,8 @@ public final class MockMvcTester {
}
/**
* Return a new {@link MockMvcTester} instance using the specified
* {@linkplain HttpMessageConverter message converters}.
* Return a new instance using the specified {@linkplain HttpMessageConverter
* message converters}.
* <p>If none are specified, only basic assertions on the response body can
* be performed. Consider registering a suitable JSON converter for asserting
* against JSON data structures.
@ -200,8 +216,105 @@ public final class MockMvcTester {
}
/**
* Perform a request and return a {@link MvcTestResult result} that can be
* used with standard {@link org.assertj.core.api.Assertions AssertJ} assertions.
* Prepare an HTTP GET request.
* <p>The returned builder can be wrapped in {@code assertThat} to enable
* assertions on the result. For multi-statements assertions, use
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
* result.
* @return a request builder for specifying the target URI
*/
public MockMvcRequestBuilder get() {
return method(HttpMethod.GET);
}
/**
* Prepare an HTTP HEAD request.
* <p>The returned builder can be wrapped in {@code assertThat} to enable
* assertions on the result. For multi-statements assertions, use
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
* result.
* @return a request builder for specifying the target URI
*/
public MockMvcRequestBuilder head() {
return method(HttpMethod.HEAD);
}
/**
* Prepare an HTTP POST request.
* <p>The returned builder can be wrapped in {@code assertThat} to enable
* assertions on the result. For multi-statements assertions, use
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
* result.
* @return a request builder for specifying the target URI
*/
public MockMvcRequestBuilder post() {
return method(HttpMethod.POST);
}
/**
* Prepare an HTTP PUT request.
* <p>The returned builder can be wrapped in {@code assertThat} to enable
* assertions on the result. For multi-statements assertions, use
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
* result.
* @return a request builder for specifying the target URI
*/
public MockMvcRequestBuilder put() {
return method(HttpMethod.PUT);
}
/**
* Prepare an HTTP PATCH request.
* <p>The returned builder can be wrapped in {@code assertThat} to enable
* assertions on the result. For multi-statements assertions, use
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
* result.
* @return a request builder for specifying the target URI
*/
public MockMvcRequestBuilder patch() {
return method(HttpMethod.PATCH);
}
/**
* Prepare an HTTP DELETE request.
* <p>The returned builder can be wrapped in {@code assertThat} to enable
* assertions on the result. For multi-statements assertions, use
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
* result.
* @return a request builder for specifying the target URI
*/
public MockMvcRequestBuilder delete() {
return method(HttpMethod.DELETE);
}
/**
* Prepare an HTTP OPTIONS request.
* <p>The returned builder can be wrapped in {@code assertThat} to enable
* assertions on the result. For multi-statements assertions, use
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
* result.
* @return a request builder for specifying the target URI
*/
public MockMvcRequestBuilder options() {
return method(HttpMethod.OPTIONS);
}
/**
* Prepare a request for the specified {@code HttpMethod}.
* <p>The returned builder can be wrapped in {@code assertThat} to enable
* assertions on the result. For multi-statements assertions, use
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
* result.
* @return a request builder for specifying the target URI
*/
public MockMvcRequestBuilder method(HttpMethod method) {
return new MockMvcRequestBuilder(method);
}
/**
* Perform a request using {@link MockMvcRequestBuilders} and return a
* {@link MvcTestResult result} that can be used with standard
* {@link org.assertj.core.api.Assertions AssertJ} assertions.
* <p>Use static methods of {@link MockMvcRequestBuilders} to prepare the
* request, wrapping the invocation in {@code assertThat}. The following
* asserts that a {@linkplain MockMvcRequestBuilders#get(URI) GET} request
@ -226,6 +339,8 @@ public final class MockMvcTester {
* {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders}
* @return an {@link MvcTestResult} to be wrapped in {@code assertThat}
* @see MockMvc#perform(RequestBuilder)
* @see #get()
* @see #post()
*/
public MvcTestResult perform(RequestBuilder requestBuilder) {
Object result = getMvcResultOrFailure(requestBuilder);
@ -259,4 +374,25 @@ public final class MockMvcTester {
.findFirst().orElse(null);
}
/**
* A builder for {@link MockHttpServletRequest} that supports AssertJ.
*/
public final class MockMvcRequestBuilder extends AbstractMockHttpServletRequestBuilder<MockMvcRequestBuilder>
implements AssertProvider<MvcTestResultAssert> {
private MockMvcRequestBuilder(HttpMethod httpMethod) {
super(httpMethod);
}
public MvcTestResult exchange() {
return perform(this);
}
@Override
public MvcTestResultAssert assertThat() {
return new MvcTestResultAssert(exchange(), MockMvcTester.this.jsonMessageConverter);
}
}
}

View File

@ -0,0 +1,94 @@
/*
* 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 org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration tests for {@link MockMvcTester} that use the methods that
* integrate with {@link MockMvc} way of building the requests and
* asserting the responses.
*
* @author Stephane Nicoll
*/
@SpringJUnitConfig
@WebAppConfiguration
class MockMvcTesterCompatibilityIntegrationTests {
private final MockMvcTester mvc;
MockMvcTesterCompatibilityIntegrationTests(@Autowired WebApplicationContext wac) {
this.mvc = MockMvcTester.from(wac);
}
@Test
void performGet() {
assertThat(this.mvc.perform(get("/greet"))).hasStatusOk();
}
@Test
void performGetWithInvalidMediaTypeAssertion() {
MvcTestResult result = this.mvc.perform(get("/greet"));
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(result).hasContentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.withMessageContaining("is compatible with 'application/json'");
}
@Test
void assertHttpStatusCode() {
assertThat(this.mvc.get().uri("/greet")).matches(status().isOk());
}
@Configuration
@EnableWebMvc
@Import(TestController.class)
static class WebConfiguration {
}
@RestController
static class TestController {
@GetMapping(path = "/greet", produces = "text/plain")
String greet() {
return "hello";
}
@GetMapping(path = "/message", produces = MediaType.APPLICATION_JSON_VALUE)
String message() {
return "{\"message\": \"hello\"}";
}
}
}

View File

@ -43,7 +43,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.Person;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
@ -63,9 +63,8 @@ import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.InstanceOfAssertFactories.map;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Integration tests for {@link MockMvcTester}.
@ -77,10 +76,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@WebAppConfiguration
public class MockMvcTesterIntegrationTests {
private final MockMvcTester mockMvc;
private final MockMvcTester mvc;
MockMvcTesterIntegrationTests(WebApplicationContext wac) {
this.mockMvc = MockMvcTester.from(wac);
this.mvc = MockMvcTester.from(wac);
}
@Nested
@ -88,24 +87,24 @@ public class MockMvcTesterIntegrationTests {
@Test
void hasAsyncStartedTrue() {
assertThat(perform(get("/callable").accept(MediaType.APPLICATION_JSON)))
assertThat(mvc.get().uri("/callable").accept(MediaType.APPLICATION_JSON))
.request().hasAsyncStarted(true);
}
@Test
void hasAsyncStartedFalse() {
assertThat(perform(get("/greet"))).request().hasAsyncStarted(false);
assertThat(mvc.get().uri("/greet")).request().hasAsyncStarted(false);
}
@Test
void attributes() {
assertThat(perform(get("/greet"))).request().attributes()
assertThat(mvc.get().uri("/greet")).request().attributes()
.containsKey(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
@Test
void sessionAttributes() {
assertThat(perform(get("/locale"))).request().sessionAttributes()
assertThat(mvc.get().uri("/locale")).request().sessionAttributes()
.containsOnly(entry("locale", Locale.UK));
}
}
@ -116,17 +115,17 @@ public class MockMvcTesterIntegrationTests {
@Test
void containsCookie() {
Cookie cookie = new Cookie("test", "value");
assertThat(performWithCookie(cookie, get("/greet"))).cookies().containsCookie("test");
assertThat(withCookie(cookie).get().uri("/greet")).cookies().containsCookie("test");
}
@Test
void hasValue() {
Cookie cookie = new Cookie("test", "value");
assertThat(performWithCookie(cookie, get("/greet"))).cookies().hasValue("test", "value");
assertThat(withCookie(cookie).get().uri("/greet")).cookies().hasValue("test", "value");
}
private MvcTestResult performWithCookie(Cookie cookie, MockHttpServletRequestBuilder request) {
MockMvcTester mockMvc = MockMvcTester.of(List.of(new TestController()), builder -> builder.addInterceptors(
private MockMvcTester withCookie(Cookie cookie) {
return MockMvcTester.of(List.of(new TestController()), builder -> builder.addInterceptors(
new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
@ -134,7 +133,6 @@ public class MockMvcTesterIntegrationTests {
return true;
}
}).build());
return mockMvc.perform(request);
}
}
@ -143,12 +141,12 @@ public class MockMvcTesterIntegrationTests {
@Test
void statusOk() {
assertThat(perform(get("/greet"))).hasStatusOk();
assertThat(mvc.get().uri("/greet")).hasStatusOk();
}
@Test
void statusSeries() {
assertThat(perform(get("/greet"))).hasStatus2xxSuccessful();
assertThat(mvc.get().uri("/greet")).hasStatus2xxSuccessful();
}
}
@ -158,13 +156,13 @@ public class MockMvcTesterIntegrationTests {
@Test
void shouldAssertHeader() {
assertThat(perform(get("/greet")))
assertThat(mvc.get().uri("/greet"))
.hasHeader("Content-Type", "text/plain;charset=ISO-8859-1");
}
@Test
void shouldAssertHeaderWithCallback() {
assertThat(perform(get("/greet"))).headers().satisfies(textContent("ISO-8859-1"));
assertThat(mvc.get().uri("/greet")).headers().satisfies(textContent("ISO-8859-1"));
}
private Consumer<HttpHeaders> textContent(String charset) {
@ -179,33 +177,33 @@ public class MockMvcTesterIntegrationTests {
@Test
void hasViewName() {
assertThat(perform(get("/persons/{0}", "Andy"))).hasViewName("persons/index");
assertThat(mvc.get().uri("/persons/{0}", "Andy")).hasViewName("persons/index");
}
@Test
void viewNameWithCustomAssertion() {
assertThat(perform(get("/persons/{0}", "Andy"))).viewName().startsWith("persons");
assertThat(mvc.get().uri("/persons/{0}", "Andy")).viewName().startsWith("persons");
}
@Test
void containsAttributes() {
assertThat(perform(post("/persons").param("name", "Andy"))).model()
assertThat(mvc.post().uri("/persons").param("name", "Andy")).model()
.containsOnlyKeys("name").containsEntry("name", "Andy");
}
@Test
void hasErrors() {
assertThat(perform(post("/persons"))).model().hasErrors();
assertThat(mvc.post().uri("/persons")).model().hasErrors();
}
@Test
void hasAttributeErrors() {
assertThat(perform(post("/persons"))).model().hasAttributeErrors("person");
assertThat(mvc.post().uri("/persons")).model().hasAttributeErrors("person");
}
@Test
void hasAttributeErrorsCount() {
assertThat(perform(post("/persons"))).model().extractingBindingResult("person").hasErrorsCount(1);
assertThat(mvc.post().uri("/persons")).model().extractingBindingResult("person").hasErrorsCount(1);
}
}
@ -215,7 +213,7 @@ public class MockMvcTesterIntegrationTests {
@Test
void containsAttributes() {
assertThat(perform(post("/persons").param("name", "Andy"))).flash()
assertThat(mvc.post().uri("/persons").param("name", "Andy")).flash()
.containsOnlyKeys("message").hasEntrySatisfying("message",
value -> assertThat(value).isInstanceOfSatisfying(String.class,
stringValue -> assertThat(stringValue).startsWith("success")));
@ -227,31 +225,31 @@ public class MockMvcTesterIntegrationTests {
@Test
void asyncResult() {
assertThat(perform(get("/callable").accept(MediaType.APPLICATION_JSON)))
assertThat(mvc.get().uri("/callable").accept(MediaType.APPLICATION_JSON))
.asyncResult().asInstanceOf(map(String.class, Object.class))
.containsOnly(entry("key", "value"));
}
@Test
void stringContent() {
assertThat(perform(get("/greet"))).body().asString().isEqualTo("hello");
assertThat(mvc.get().uri("/greet")).body().asString().isEqualTo("hello");
}
@Test
void jsonPathContent() {
assertThat(perform(get("/message"))).bodyJson()
assertThat(mvc.get().uri("/message")).bodyJson()
.extractingPath("$.message").asString().isEqualTo("hello");
}
@Test
void jsonContentCanLoadResourceFromClasspath() {
assertThat(perform(get("/message"))).bodyJson().isLenientlyEqualTo(
assertThat(mvc.get().uri("/message")).bodyJson().isLenientlyEqualTo(
new ClassPathResource("message.json", MockMvcTesterIntegrationTests.class));
}
@Test
void jsonContentUsingResourceLoaderClass() {
assertThat(perform(get("/message"))).bodyJson().withResourceLoadClass(MockMvcTesterIntegrationTests.class)
assertThat(mvc.get().uri("/message")).bodyJson().withResourceLoadClass(MockMvcTesterIntegrationTests.class)
.isLenientlyEqualTo("message.json");
}
@ -262,22 +260,22 @@ public class MockMvcTesterIntegrationTests {
@Test
void handlerOn404() {
assertThat(perform(get("/unknown-resource"))).handler().isNull();
assertThat(mvc.get().uri("/unknown-resource")).handler().isNull();
}
@Test
void hasType() {
assertThat(perform(get("/greet"))).handler().hasType(TestController.class);
assertThat(mvc.get().uri("/greet")).handler().hasType(TestController.class);
}
@Test
void isMethodHandler() {
assertThat(perform(get("/greet"))).handler().isMethodHandler();
assertThat(mvc.get().uri("/greet")).handler().isMethodHandler();
}
@Test
void isInvokedOn() {
assertThat(perform(get("/callable"))).handler()
assertThat(mvc.get().uri("/callable")).handler()
.isInvokedOn(AsyncController.class, AsyncController::getCallable);
}
@ -288,31 +286,31 @@ public class MockMvcTesterIntegrationTests {
@Test
void doesNotHaveUnresolvedException() {
assertThat(perform(get("/greet"))).doesNotHaveUnresolvedException();
assertThat(mvc.get().uri("/greet")).doesNotHaveUnresolvedException();
}
@Test
void hasUnresolvedException() {
assertThat(perform(get("/error/1"))).hasUnresolvedException();
assertThat(mvc.get().uri("/error/1")).hasUnresolvedException();
}
@Test
void doesNotHaveUnresolvedExceptionWithUnresolvedException() {
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(perform(get("/error/1"))).doesNotHaveUnresolvedException())
.isThrownBy(() -> assertThat(mvc.get().uri("/error/1")).doesNotHaveUnresolvedException())
.withMessage("Expected request to succeed, but it failed");
}
@Test
void hasUnresolvedExceptionWithoutUnresolvedException() {
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(perform(get("/greet"))).hasUnresolvedException())
.isThrownBy(() -> assertThat(mvc.get().uri("/greet")).hasUnresolvedException())
.withMessage("Expected request to fail, but it succeeded");
}
@Test
void unresolvedExceptionWithFailedRequest() {
assertThat(perform(get("/error/1"))).unresolvedException()
assertThat(mvc.get().uri("/error/1")).unresolvedException()
.isInstanceOf(ServletException.class)
.cause().isInstanceOf(IllegalStateException.class).hasMessage("Expected");
}
@ -320,7 +318,7 @@ public class MockMvcTesterIntegrationTests {
@Test
void unresolvedExceptionWithSuccessfulRequest() {
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(perform(get("/greet"))).unresolvedException())
.isThrownBy(() -> assertThat(mvc.get().uri("/greet")).unresolvedException())
.withMessage("Expected request to fail, but it succeeded");
}
@ -406,7 +404,7 @@ public class MockMvcTesterIntegrationTests {
private void testAssertionFailureWithUnresolvableException(Consumer<MvcTestResult> assertions) {
MvcTestResult result = perform(get("/error/1"));
MvcTestResult result = mvc.get().uri("/error/1").exchange();
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertions.accept(result))
.withMessageContainingAll("Request failed unexpectedly:",
@ -418,49 +416,49 @@ public class MockMvcTesterIntegrationTests {
@Test
void hasForwardUrl() {
assertThat(perform(get("/persons/John"))).hasForwardedUrl("persons/index");
assertThat(mvc.get().uri("/persons/John")).hasForwardedUrl("persons/index");
}
@Test
void hasRedirectUrl() {
assertThat(perform(post("/persons").param("name", "Andy"))).hasStatus(HttpStatus.FOUND)
assertThat(mvc.post().uri("/persons").param("name", "Andy")).hasStatus(HttpStatus.FOUND)
.hasRedirectedUrl("/persons/Andy");
}
@Test
void satisfiesAllowsAdditionalAssertions() {
assertThat(this.mockMvc.perform(get("/greet"))).satisfies(result -> {
assertThat(mvc.get().uri("/greet")).satisfies(result -> {
assertThat(result).isInstanceOf(MvcTestResult.class);
assertThat(result).hasStatusOk();
});
}
@Test
void resultMatcherCanBeReused() {
assertThat(this.mockMvc.perform(get("/greet"))).matches(status().isOk());
void resultMatcherCanBeReused() throws Exception {
MvcTestResult result = mvc.get().uri("/greet").exchange();
ResultMatcher matcher = mock(ResultMatcher.class);
assertThat(result).matches(matcher);
verify(matcher).match(result.getMvcResult());
}
@Test
void resultMatcherFailsWithDedicatedException() {
ResultMatcher matcher = result -> assertThat(result.getResponse().getStatus())
.isEqualTo(HttpStatus.NOT_FOUND.value());
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> assertThat(this.mockMvc.perform(get("/greet")))
.matches(status().isNotFound()))
.withMessageContaining("Status expected:<404> but was:<200>");
.isThrownBy(() -> assertThat(mvc.get().uri("/greet"))
.matches(matcher))
.withMessageContaining("expected: 404").withMessageContaining(" but was: 200");
}
@Test
void shouldApplyResultHandler() { // Spring RESTDocs example
AtomicBoolean applied = new AtomicBoolean();
assertThat(this.mockMvc.perform(get("/greet"))).apply(result -> applied.set(true));
assertThat(mvc.get().uri("/greet")).apply(result -> applied.set(true));
assertThat(applied).isTrue();
}
private MvcTestResult perform(MockHttpServletRequestBuilder builder) {
return this.mockMvc.perform(builder);
}
@Configuration
@EnableWebMvc
@Import({ TestController.class, PersonController.class, AsyncController.class,