Im currently writing some basic unit tests for my REST-Endpoints. I use Mockito for that. Here one example:
@MockBean
private MyService service;
@Test
public void getItems() {
Flux<Item> result = Flux.create(sink -> {
sink.next(new Item("1"));
sink.next(new Item("2"));
sink.complete();
});
Mono<ItemParams> params = Mono.just(new ItemParams("1"));
Mockito.when(this.service.getItems(params)).thenReturn(result);
this.webClient.post().uri("/items")
.accept(MediaType.APPLICATION_STREAM_JSON)
.contentType(MediaType.APPLICATION_STREAM_JSON)
.body(BodyInserters.fromPublisher(params, ItemParams.class))
.exchange()
.expectStatus().isOk()
.expectBodyList(Item.class).isEqualTo(Objects.requireNonNull(result.collectList().block()));
}
This implementation leads to the following error:
java.lang.AssertionError: Response body expected:<[Item(name=1), Item(name=2)]> but was:<[]>
> POST /items
> WebTestClient-Request-Id: [1]
> Accept: [application/stream+json]
> Content-Type: [application/stream+json]
Content not available yet
< 200 OK
< Content-Type: [application/stream+json;charset=UTF-8]
No content
When I exchange the parameter in the Mockito Statement with Mockito.any()
Mockito.when(this.service.getItems(Mockito.any())).thenReturn(result);
The test runs through successfully.
That means that for some reason the params
I put into the Mockito
Statement isnt equal to the params
object which I put into BodyInserters.fromPublisher(params, ItemParams.class)
How am I supposed to test my functionality then?
EDIT
REST-Endpoint
@PostMapping(path = "/items", consumes = MediaType.APPLICATION_STREAM_JSON_VALUE, produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
public Flux<Item> getItems(@Valid @RequestBody Mono<ItemParams> itemParms) {
return this.service.getItems(itemParms);
}
Mono is more relatable to the Optional class in Java since it contains 0 or 1 value, and Flux is more relatable to List since it can have N number of values.
A Flux object represents a reactive sequence of 0.. N items, while a Mono object represents a single-value-or-empty (0..1) result. This distinction carries a bit of semantic information into the type, indicating the rough cardinality of the asynchronous processing.
We have two main options for mocking in our tests: Use Mockito to mimic the behavior of WebClient. Use WebClient for real, but mock the service it calls by using MockWebServer (okhttp)
Wouldn't the actual object, @RequestBody Mono<ItemParams> itemParms
, be different than the one you create and pass in the test?
You could take advantage of thenAnswer
in order to verify the content of the object that is actually passed to the service:
Mockito.when(this.service.getItems(Mockito.any()))
.thenAnswer(new Answer<Flux<Item>>() {
@Override
public Flux<Item> answer(InvocationOnMock invocation) throws Throwable {
Mono<ItemParams> mono = (Mono<ItemParams>)invocation.getArgument(0);
if(/* verify that paseed mono contains new ItemParams("1")*/){
return result;
}
return null;
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With