使用Mockito测试Mono和Flux

8

我目前正在为我的REST端点编写一些基本的单元测试。我使用Mockito进行测试。下面是一个示例:

@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()));
}

这个实现会导致以下错误:

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

当我将Mockito语句中的参数替换为Mockito.any()
Mockito.when(this.service.getItems(Mockito.any())).thenReturn(result);

测试成功运行。 这意味着由于我在Mockito语句中放入的“params”与我放入BodyInserters.fromPublisher(params,ItemParams.class)中的“params”对象不相等,因此出现了这种情况。 那么我应该如何测试我的功能呢? 编辑 REST端点
@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);
}

断言在哪里?你能补充一下你是如何模拟服务的吗? - Maciej Kowalski
我添加了 MockBean,并且断言是 .expectBodyList(Item.class).isEqualTo(Objects.requireNonNull(result.collectList().block())); - Mulgard
尝试展示端点实现...至少是重要部分。 - Maciej Kowalski
我已添加了 REST 端点。 - Mulgard
1个回答

5

实际对象@RequestBody Mono<ItemParams> itemParms会不会和你在测试中创建和传递的对象不同?

你可以利用thenAnswer来验证实际传递给服务的对象内容:

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;
    }
});

我不太明白你的意思。我想确保一个 Mono<ItemParams> 被传递到函数中。这就是为什么我创建了一个并将其放入我的 Mockito.whenBodyInserter 中。这意味着它肯定是同一个对象。 - Mulgard
所以你只想确保传递的是 Mono 类型的对象?无论其内容如何? - Maciej Kowalski
不,不管它的内容是什么,这就是为什么我使用 Mono<ItemParams> 而不是 MonoMono 的内容必须是 ItemParams 类型。 - Mulgard
不太确定该类的api / equals方法是什么,但基于传递对象内容的结果通常使用thenAnswer功能来解决。在这里,使用when..then的正常对象期望似乎不起作用,因此会出现异常。 - Maciej Kowalski
无论如何,您的解决方案可以强制测试仅接受Mono<ItemParams>作为输入参数。 - Mulgard
即使这已经被接受为答案,我不认为这是一个好的方法。为了编写适当的测试,您不应该在测试中引入额外的逻辑。您只需要准备好测试替身、准备预期输入、调用被测试单元并验证预期输出,就可以了。 - pesoklp13

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接