Skip to content

Streamline WebTestClient creation and customization in integration tests #15132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
rstoyanchev opened this issue Nov 8, 2018 · 6 comments
Closed
Assignees
Labels
type: enhancement A general enhancement
Milestone

Comments

@rstoyanchev
Copy link
Contributor

rstoyanchev commented Nov 8, 2018

The docs show an example of using the WebTestClient but nothing to show how to customize its configuration like this section does for the WebClient. I did find WebTestClientBuilderCustomizer but that should be explained in the reference and if there are other options, those could be mentioned too.

This is based on related comment under SPR-17039.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Nov 8, 2018
@rstoyanchev
Copy link
Contributor Author

rstoyanchev commented Nov 9, 2018

I did not have success with WebTestClientBuilderCustomizer for some reason, it wasn't picked up, so having a copy and paste example in the docs would be great.

I tried autowiring WebTestClient.Builder with no success either. It's probably not intended to be used in this way.

I did come up with the following:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWebTestClient
public class LocationServiceTests {

	@Autowired
	private WebTestClient client;


	@Before
	public void setup() {
		DefaultUriBuilderFactory builderFactory = new DefaultUriBuilderFactory();
		builderFactory.setEncodingMode(URI_COMPONENT);
		this.client = this.client.mutate().uriBuilderFactory(builderFactory).build();
	}


	@Test
	public void exampleTest() {
		this.client.get().uri("/").exchange().expectStatus().isOk()
				.expectBody(String.class).isEqualTo("Hello World");
	}

}

@philwebb philwebb added type: documentation A documentation update and removed status: waiting-for-triage An issue we've not yet triaged labels Nov 9, 2018
@philwebb philwebb added this to the 2.1.x milestone Nov 9, 2018
@michaldo
Copy link
Contributor

@rstoyanchev, your proposal does not apply case
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
In that case test framework setup host and port inside WebTestClient and @Before mutate() clears that data. Eventually, client does not know hostname nor port.

That is why I wrote The only way I found is drop autoconfigured webclient and create custom one.

@rstoyanchev
Copy link
Contributor Author

rstoyanchev commented Nov 21, 2018

Oh, I see now. You want to customize the UriBuilderFactory but that overrides the baseUrl set up by Boot. Hm, I'm not sure I see a good way to do this. Effectively both the test code and Boot are competing to customize the UriBuilderFactory.

@wilkinsona
Copy link
Member

The WebTestClient produced by @AutoConfigureWebTestClient can be customized using a @TestConfiguration static inner-class that defines a @Bean:

@TestConfiguration
static class BuilderCustomizerConfiguration {

	@Bean
	WebTestClientBuilderCustomizer customizer() {
		return (builder) -> {
			builder.filter((request, next) -> {
				System.out.println(request.method() + " " + request.url());
				return next.exchange(request);
			});
		};
	}

}

This is an imperfect solution, though. Using @AutoConfigureWebTestClient means that WebTestClientContextCustomizer backs off. The auto-configured WebTestClient is found in the bean factory so it does not create a WebTestClient bean with a customized base URL. The auto-configured WebTestClient still works despite not knowing the port on which the server is running as it uses HttpHandlerConnector, calling the HttpHandler directly rather than going via the network stack and HTTP.

I think this is more than a documentation issue as I don't think we have a particularly good solution to document at the moment.

@wilkinsona wilkinsona added the for: team-attention An issue we'd like other members of the team to review label Sep 2, 2019
@philwebb philwebb modified the milestones: 2.1.x, 2.2.x Sep 4, 2019
@philwebb philwebb added type: enhancement A general enhancement and removed for: team-attention An issue we'd like other members of the team to review type: documentation A documentation update labels Sep 4, 2019
@philwebb philwebb changed the title Documentation on WebTestClient customizations Streamline WebTestClient creation and customization in integration tests Sep 4, 2019
@wilkinsona
Copy link
Member

wilkinsona commented Sep 23, 2019

When a test is annotated with @SpringBootTest that has an embedded web environment, spring-boot-test will:

  • register a TestRestTemplate bean with a custom UriTemplateHandler that takes into account the use of SSL and the local server port. Customization is supported via a RestTemplateBuilder bean which, if present in the context, will be used to create the TestRestTemplate.
  • register a WebTestClient bean with a customized base URL that takes into account the use of SSL and the local server port. Codec customization is supported via CodecCustomizer beans found in the context. No further customization is supported.

There are some other difference in spring-boot-test-autoconfigure but things aren't directly comparable as mock testing comes into play where the servlet-based stack uses MockMvc while the reactive stack uses an application context bound WebTestClient. There's also MockRestServiceServer for testing RestTemplate usage for which the reactive stack has no equivalent. For the time being at least, I think we may be best served by focusing on the differences in spring-boot-test, specifically the lack of WebTestClient customization that's offered.

Two possible options for @SpringBootTest appear to be:

  1. If present, use a user-provided WebTestClient.Builder to create the WebTestClient
  2. Move WebTestClientBuilderCustomizer into spring-boot-test and support it when using @SpringBootTest

Of the two, the second seems the better to me. The first is problematic as, due to WebTestClient.Builder supporting server, application context, and individual handler/router function binding, it gives the user an opportunity to provide a WebTestClient.Builder that doesn't match the web environment. If we keep control of the creation of the builder, we could create one using the appropriate binding for the web environment (server bound for an embedded environment, application context bound for a mock environment) before passing it to any user-provided customizers for further customization.

@wilkinsona
Copy link
Member

Here's a proposal for the team's consideration. It implements the second of the two options listed above. It still limits support to a server bound WebTestClient. I think that expanding it to include mock environments should be considered in a separate issue as it also raises the question about MockMvc when using the Servlet stack.

@wilkinsona wilkinsona added the for: team-attention An issue we'd like other members of the team to review label Sep 23, 2019
@wilkinsona wilkinsona self-assigned this Sep 23, 2019
@wilkinsona wilkinsona modified the milestones: 2.2.x, 2.2.0.RC1 Oct 2, 2019
@wilkinsona wilkinsona removed the for: team-attention An issue we'd like other members of the team to review label Oct 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants