Spring-framework: Support of MockRestServiceServer for WebClient [SPR-15286]

Created on 24 Feb 2017  路  22Comments  路  Source: spring-projects/spring-framework

St茅phane Nicoll opened SPR-15286 and commented

Spring Boot has a @RestClientTest for pure client-side tests. We currently auto-configure MockRestServiceServer and bind any RestTemplate created by the RestTemplateBuilder to it.

This would be pretty awesome if we could port that feature to WebClient transparently if webflux is on the classpath. That would require that MockRestServiceServer can be configured with a WebClient in addition to a RestTemplate


Sub-tasks:

  • #19295 Document how to test WebClient code with a mock server

15 votes, 24 watchers

votes-jira test web declined enhancement

Most helpful comment

@membersound I appreciate the point but there is also a lot of effort involved to create, maintain, and evolve something over time and it's hard to justify given the existing alternatives. HTTP provides loose coupling so implementations can exist and evolve independently. In this case we're even talking about mocking a server and shaping its behavior. I should think that switching from one to another shouldn't be as big a deal as it might seem at first. That said MockWebServer has more stars than the Spring Framework does and I don't think it is going to be a problem with longevity.

All 22 comments

Rossen Stoyanchev commented

Indeed we'll most certainly need that. I'm putting it in the 5.1 backlog for now since I don't expect we'll manage that for 5.0.

Rossen Stoyanchev commented

In the mean time I suggest using the OkHttp MockWebServer (or similar) as do in our own WebClientIntegrationTests.

Rob Worsnop commented

OkHttp was not a good substitute for us, so we created a temporary drop-in replacement for MockRestServiceServer. It, along with supporting classes, is here: https://github.com/vmware/connectors-workspace-one/tree/master/common/connectors-test/src/main/java/com/vmware/connectors/mock

Example usage here: https://github.com/vmware/connectors-workspace-one/blob/master/connectors/jira/src/test/java/com/vmware/connectors/jira/JiraControllerTests.java

Rossen Stoyanchev commented

What did you run into, i.e. where did using OkHttp fall short?

Rob Worsnop commented

Our code does a parallel scatter-gather and aggregates the results. Because OkHttp is a "real" HTTP server, we get those results in a non-deterministic order, which makes it hard to match actual results to expected results.

I think I got around that by sorting the results with Flux.sort (in the production code, not tests) but then there was another problem: Our server returns JSON with hyperlinks pointing to the back-end it's accessing. With MockRestServiceServer, our code thinks it's talking to "http://bogus-host" and reliably formats links accordingly. With OkHttp, our server knows it's talking to "http://localhost:<random_port>", with the port changing with every test. Again, we can't easily match actual results to expected results.

Rossen Stoyanchev commented

Thanks for the feedback. Would JSONPath help in this scenario, i.e. dealing with data in non-deterministic order, and also parameterizing the host and port? On the flip side processing scatter gather in true random fashion probably makes for more realistic tests, which is desirable as long as it doesn't bring undue burden.

Rob Worsnop commented

I thought I could probably work around all of the issues somehow, but at some point I realized that creating a WebClient-compatible mock was going to end up being easier.
Also, switching from AsyncRestTemplate to WebClient was a fairly major change, so being able to use virtually the same tests as before was pretty important. It gave us reassurance that behavior hadn't changed.

Rossen Stoyanchev commented

Good to know, thanks!

Rob Worsnop commented

Quick update: I just used WebTestClient/OkHttp on a new project. It does seem _much_ better than MockMvc/MockRestServiceServer.

One thing I noticed is that ordering in JSON arrays doesn't matter because I'm calling expectBody().json() on WebTestClient. Then I went back and noticed that we're calling content().string in the old code instead of content().json. (The reasons for doing this aren't entirely stupid, but they aren't that great either.)

Now I'm wondering if rewriting the old tests using WebTestClient/OkHttp might be the best way forward, now that the old tests have confirmed that moving to WebClient hasn't broken anything.

Rossen Stoyanchev commented

It would be useful to confirm if there are any specific issues in transitioning those tests. That is if you find the time the time for that exercise :)

Craig Skinfill commented

any plans to implement this?聽 its making adopting WebFlux and WebClient harder since we can't test the same way we could with RestTemplate

Rossen Stoyanchev commented

Craig Skinfill, given the existence of the MockWebServer and others like WireMock, the idea is we might never build a MockRestServiceServer equivalent. I realize we have work to do to make this more clear in the docs, and to add some testing support for this in Spring Boot, but as far a direction, in the absence of any compelling reasons to build our own solution, MockWebServer (or similar mock server) is the present day alternative to MockRestServiceServer.

I have not done a complete analysis and comparison but it's clear that MockWebServer is in the same space and serves the same purpose, but is more widely applicable to any HTTP client, because it's exposed over HTTP through communication over a socket. So instead of using some sort of mock ClientHttpConnector with canned responses, you get to keep the HTTP client underneath (e.g. ReactorClientHttpConnector) and that leads to more complete testing. Over an actual socket it's more natural to simulate slow network conditions, chunked responses, and the like, and then in turn to test the impact with the actual HTTP client in charge of processing content on the wire. Even if we did create our own solution again, I'd consider strongly a similar approach but then again, why re-create what's available? Last but not least, it's worth mentioning also that MockWebServer has a large community which is important for maturity, learning resources, and feedback from diverse use cases.

Craig Skinfill commented

Rossen Stoyanchev thanks for the explanation.聽 that makes sense to me.聽 Is there a preferred, from spring reactor perspective, mock server to consider?聽 Or any that have a closer integration with spring?

Thanks again.

Rossen Stoyanchev commented

Craig Skinfill we're using MockWebServer for our own tests, and it suits our needs so far. I don't have experience with others.

I think we probably need to think about Boot support for integration tests for code that uses the WebClient. Brian Clozel should we create a ticket for that in Boot to explore what such support could look like? Aside from that I don't see much need for Spring-specific configuration since client and server communicate over a socket, they're pretty decoupled.

Using WebClient for client-side requests is pretty useless if there is no proper mock in spring for testing our code. Having rewritten my RestTemplate client code to WebClient, I'm disappointed there is no replacement for MockRestServiceServer yet...

@membersound Have you used wiremock ?
WireMock can be used for mocking the responses for WebClient. Check this code repo which has examples for the same.
Repo:
https://github.com/code-with-dilip/learn-wiremock/tree/master/learn-wiremock-spring-cloud
TestCases:
https://github.com/code-with-dilip/learn-wiremock/blob/master/learn-wiremock-spring-cloud/src/test/java/com/learnwiremock/service/UserServiceWireMockRuleTest.java.
Cheers!

While this might work, I'd prefer relying on some kind of "official" framework, eg something that's included and maintained by spring themselves. Otherwise, if WebClient evolves by time, and WireMock is left behind, that might led to rewrites for all of my projects. As we all know, time is money, so that's too risky...

@membersound I appreciate the point but there is also a lot of effort involved to create, maintain, and evolve something over time and it's hard to justify given the existing alternatives. HTTP provides loose coupling so implementations can exist and evolve independently. In this case we're even talking about mocking a server and shaping its behavior. I should think that switching from one to another shouldn't be as big a deal as it might seem at first. That said MockWebServer has more stars than the Spring Framework does and I don't think it is going to be a problem with longevity.

Is it listed somewhere in the official docs that MockWebServer is the recommended solution? I've spent a few days now looking for an official Spring version or trying to get existing stuff to work and finally stumbled on this thread..

@bclozel I see it now...thanks.

Closing as we have no further plans at this time.

Was this page helpful?
0 / 5 - 0 ratings