MockMvcHttpConnector supports RequestPostProcessor's

Closes gh-31298
This commit is contained in:
rstoyanchev 2023-09-22 14:51:53 +01:00
parent 0f6b018e97
commit af7fe013b6
5 changed files with 121 additions and 18 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -80,7 +80,7 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
private final WebHttpHandlerBuilder httpHandlerBuilder; private final WebHttpHandlerBuilder httpHandlerBuilder;
@Nullable @Nullable
private final ClientHttpConnector connector; private ClientHttpConnector connector;
@Nullable @Nullable
private String baseUrl; private String baseUrl;
@ -277,6 +277,12 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
return this; return this;
} }
@Override
public WebTestClient.Builder clientConnector(ClientHttpConnector connector) {
this.connector = connector;
return this;
}
@Override @Override
public WebTestClient build() { public WebTestClient build() {
ClientHttpConnector connectorToUse = this.connector; ClientHttpConnector connectorToUse = this.connector;

View File

@ -512,6 +512,18 @@ public interface WebTestClient {
*/ */
Builder responseTimeout(Duration timeout); Builder responseTimeout(Duration timeout);
/**
* Set the {@link ClientHttpConnector} to use.
* <p>By default, this is initialized and set internally. However, the
* connector may also be prepared externally and passed via
* {@link WebTestClient#bindToServer(ClientHttpConnector)} such as for
* {@code MockMvcWebTestClient} tests, and in that case you can use this
* from {@link #mutateWith(WebTestClientConfigurer)} to replace it.
* @param connector the connector to use
* @since 6.1
*/
Builder clientConnector(ClientHttpConnector connector);
/** /**
* Apply the given configurer to this builder instance. * Apply the given configurer to this builder instance.
* <p>This can be useful for applying pre-packaged customizations. * <p>This can be useful for applying pre-packaged customizations.

View File

@ -19,6 +19,7 @@ package org.springframework.test.web.servlet.client;
import java.io.StringWriter; import java.io.StringWriter;
import java.net.URI; import java.net.URI;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -56,6 +57,7 @@ import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@ -82,9 +84,16 @@ public class MockMvcHttpConnector implements ClientHttpConnector {
private final MockMvc mockMvc; private final MockMvc mockMvc;
private final List<RequestPostProcessor> requestPostProcessors;
public MockMvcHttpConnector(MockMvc mockMvc) { public MockMvcHttpConnector(MockMvc mockMvc) {
this(mockMvc, Collections.emptyList());
}
private MockMvcHttpConnector(MockMvc mockMvc, List<RequestPostProcessor> requestPostProcessors) {
this.mockMvc = mockMvc; this.mockMvc = mockMvc;
this.requestPostProcessors = new ArrayList<>(requestPostProcessors);
} }
@ -135,6 +144,8 @@ public class MockMvcHttpConnector implements ClientHttpConnector {
} }
} }
this.requestPostProcessors.forEach(requestBuilder::with);
return requestBuilder; return requestBuilder;
} }
@ -207,6 +218,15 @@ public class MockMvcHttpConnector implements ClientHttpConnector {
return clientResponse; return clientResponse;
} }
/**
* Create a new instance that applies the given {@link RequestPostProcessor}s
* to performed requests.
* @since 6.1
*/
public MockMvcHttpConnector with(List<RequestPostProcessor> postProcessors) {
return new MockMvcHttpConnector(this.mockMvc, postProcessors);
}
private static class MockMvcServerClientHttpResponse private static class MockMvcServerClientHttpResponse
extends MockClientHttpResponse implements MockServerClientHttpResponse { extends MockClientHttpResponse implements MockServerClientHttpResponse {

View File

@ -17,11 +17,17 @@
package org.springframework.test.web.servlet.samples.client.standalone; package org.springframework.test.web.servlet.samples.client.standalone;
import java.security.Principal; import java.security.Principal;
import java.util.List;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.reactive.server.WebTestClientConfigurer;
import org.springframework.test.web.servlet.client.MockMvcHttpConnector;
import org.springframework.test.web.servlet.client.MockMvcWebTestClient; import org.springframework.test.web.servlet.client.MockMvcWebTestClient;
import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
@ -30,6 +36,7 @@ import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -50,16 +57,16 @@ public class FrameworkExtensionTests {
@Test @Test
public void fooHeader() { public void fooHeader() {
this.client.get().uri("/") this.client.mutateWith(headers().foo("a=b"))
.header("Foo", "a=b") .get().uri("/")
.exchange() .exchange()
.expectBody(String.class).isEqualTo("Foo"); .expectBody(String.class).isEqualTo("Foo");
} }
@Test @Test
public void barHeader() { public void barHeader() {
this.client.get().uri("/") this.client.mutateWith(headers().bar("a=b"))
.header("Bar", "a=b") .get().uri("/")
.exchange() .exchange()
.expectBody(String.class).isEqualTo("Bar"); .expectBody(String.class).isEqualTo("Bar");
} }
@ -68,6 +75,68 @@ public class FrameworkExtensionTests {
return new TestMockMvcConfigurer(); return new TestMockMvcConfigurer();
} }
private static TestWebTestClientConfigurer headers() {
return new TestWebTestClientConfigurer();
}
/**
* Test WebTestClientConfigurer that re-creates the MockMvcHttpConnector
* with a {@code TestRequestPostProcessor}.
*/
private static class TestWebTestClientConfigurer implements WebTestClientConfigurer {
private final TestRequestPostProcessor requestPostProcessor = new TestRequestPostProcessor();
public TestWebTestClientConfigurer foo(String value) {
this.requestPostProcessor.foo(value);
return this;
}
public TestWebTestClientConfigurer bar(String value) {
this.requestPostProcessor.bar(value);
return this;
}
@Override
public void afterConfigurerAdded(
WebTestClient.Builder builder, WebHttpHandlerBuilder httpHandlerBuilder,
ClientHttpConnector connector) {
if (connector instanceof MockMvcHttpConnector mockMvcConnector) {
builder.clientConnector(mockMvcConnector.with(List.of(this.requestPostProcessor)));
}
}
}
/**
* Test {@code RequestPostProcessor} for custom headers.
*/
private static class TestRequestPostProcessor implements RequestPostProcessor {
private final HttpHeaders headers = new HttpHeaders();
public TestRequestPostProcessor foo(String value) {
this.headers.add("Foo", value);
return this;
}
public TestRequestPostProcessor bar(String value) {
this.headers.add("Bar", value);
return this;
}
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
for (String headerName : this.headers.keySet()) {
request.addHeader(headerName, this.headers.get(headerName));
}
return request;
}
}
/** /**
* Test {@code MockMvcConfigurer}. * Test {@code MockMvcConfigurer}.
@ -80,8 +149,9 @@ public class FrameworkExtensionTests {
} }
@Override @Override
public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder, public RequestPostProcessor beforeMockMvcCreated(
WebApplicationContext context) { ConfigurableMockMvcBuilder<?> builder, WebApplicationContext context) {
return request -> { return request -> {
request.setUserPrincipal(mock()); request.setUserPrincipal(mock());
return request; return request;

View File

@ -18,7 +18,6 @@ package org.springframework.test.web.servlet.samples.standalone;
import java.security.Principal; import java.security.Principal;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
@ -53,14 +52,9 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standal
*/ */
public class FrameworkExtensionTests { public class FrameworkExtensionTests {
private MockMvc mockMvc; private final MockMvc mockMvc = standaloneSetup(new SampleController()).apply(defaultSetup()).build();
@BeforeEach
public void setup() {
this.mockMvc = standaloneSetup(new SampleController()).apply(defaultSetup()).build();
}
@Test @Test
public void fooHeader() throws Exception { public void fooHeader() throws Exception {
this.mockMvc.perform(get("/").with(headers().foo("a=b"))).andExpect(content().string("Foo")); this.mockMvc.perform(get("/").with(headers().foo("a=b"))).andExpect(content().string("Foo"));
@ -81,7 +75,7 @@ public class FrameworkExtensionTests {
/** /**
* Test {@code RequestPostProcessor}. * Test {@code RequestPostProcessor} for custom headers.
*/ */
private static class TestRequestPostProcessor implements RequestPostProcessor { private static class TestRequestPostProcessor implements RequestPostProcessor {
@ -119,8 +113,9 @@ public class FrameworkExtensionTests {
} }
@Override @Override
public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder, public RequestPostProcessor beforeMockMvcCreated(
WebApplicationContext context) { ConfigurableMockMvcBuilder<?> builder, WebApplicationContext context) {
return request -> { return request -> {
request.setUserPrincipal(mock()); request.setUserPrincipal(mock());
return request; return request;