Unit testing WebClient has very low ROI. I would recommend looking at WireMock that provides very good API for testing web clients. Here are some examples
stubFor(get(url)
.withHeader(HttpHeaders.ACCEPT, containing(MediaType.APPLICATION_JSON_VALUE))
.willReturn(aResponse()
.withStatus(200)
.withBody("{}")
)
);
StepVerifier.create(service.callApi())
.expectNextCount(1)
.verifyComplete();
You could easily test both positive and negative scenarios by providing different stubs
stubFor(get(url)
.withHeader(HttpHeaders.ACCEPT, containing(MediaType.APPLICATION_JSON_VALUE))
.willReturn(aResponse()
.withStatus(500)
)
);
In addition, you could test retry logic using Scenarios or even simulate timeouts using Delays.
To initialize it you could use @AutoConfigureWireMockprovided by org.springframework.cloud:spring-cloud-contract-wiremock or, as an alternative, you can add direct dependency to WireMock and initialize it explicitly.
How to unit test Spring WebClient?
junit - How to unit test the Spring WebClient in java? - Stack Overflow
Kotlin Coroutines + Spring Boot
RestClient vs. WebClient vs RestTemplate - Using the suitable library to call REST API in Spring Boot
Is it possible to unit test a Spring WebClient by mocking it?
The only examples I come across are integration tests using MockWebserver, WireMock or Hoverfly which does not mock WebClient components but rather instantiates the whole WebClient and mocks the actual backend that the WebClient should connect to.
Unit testing WebClient has very low ROI. I would recommend looking at WireMock that provides very good API for testing web clients. Here are some examples
stubFor(get(url)
.withHeader(HttpHeaders.ACCEPT, containing(MediaType.APPLICATION_JSON_VALUE))
.willReturn(aResponse()
.withStatus(200)
.withBody("{}")
)
);
StepVerifier.create(service.callApi())
.expectNextCount(1)
.verifyComplete();
You could easily test both positive and negative scenarios by providing different stubs
stubFor(get(url)
.withHeader(HttpHeaders.ACCEPT, containing(MediaType.APPLICATION_JSON_VALUE))
.willReturn(aResponse()
.withStatus(500)
)
);
In addition, you could test retry logic using Scenarios or even simulate timeouts using Delays.
To initialize it you could use @AutoConfigureWireMockprovided by org.springframework.cloud:spring-cloud-contract-wiremock or, as an alternative, you can add direct dependency to WireMock and initialize it explicitly.
We use Wiremock to test Client functionality. It can be done in standard unit test or a SpringBoot test.
Below code explains the Unit testing in regular tests.
@Service
class MoviesClient {
private final WebClient moviesWebClient;
public Mono<Movie> findMovieById(String movieID) {
return moviesWebClient.get().uri(MOVIE_ENDPOINT+"/"+movieID).bodyValue(job).retrieve()
.bodyToMono(Movie.class);
}
}
Now as we see MoviesClient needs webClient to call the movie service and get movie.
@ExtendWith({MockitoExtension.class})
class MoviesClientTest {
private MoviesClient moviesClient;
@RegisterExtension
private static final WireMockExtension MOVIES_WM =
WireMockExtension.newInstance().options(wireMockConfig().port("8086")).build();
@BeforeEach
void setup() {
var webClient = WebClient.builder().baseUrl("http://localhost:8086")
.defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE).build();
moviesClient = new MoviesClient(moviesClient)
MOVIES_WM.stubFor(get(MOVIE_ENDPOINT+"/"+"1234")).withHeader(CONTENT_TYPE, matching(APPLICATION_JSON_VALUE))willReturn(aResponse().withStatus(OK.value()).withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.withBody("Your json response body comes here")));
}
}
Now you can write your tests to call the method on client which in turn will make a webclient call which will be stubbed to wiremock server.
@Test
void testMoviesEndPoint() {
StepVerifier.create(moviesClient.findMovieById(1234)
.expectNext(movie -> assertThat(movie).getId.equals(1234))
.verifyComplete();
FLUENT_WM.verify(1, postRequestedFor(urlEqualTo(MOVIE_ENDPOINT+"/"+"1234")));
}