Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Mono and Flux using Mockito

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);
}
like image 618
Mulgard Avatar asked Jan 22 '18 09:01

Mulgard


People also ask

What is the difference between flux and Mono?

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.

What is mono and flux in Java?

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.

How do you mock a WebClient call?

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)


1 Answers

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;
    }
});
like image 169
Maciej Kowalski Avatar answered Sep 28 '22 15:09

Maciej Kowalski