【问题标题】:Testing Mono and Flux using Mockito使用 Mockito 测试 Mono 和 Flux
【发布时间】:2025-12-19 18:50:11
【问题描述】:

我目前正在为我的 REST-Endpoints 编写一些基本的单元测试。我为此使用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);
}

【问题讨论】:

  • 断言在哪里?您还可以补充一下您是如何模拟该服务的吗?
  • 我添加了MockBean,断言是.expectBodyList(Item.class).isEqualTo(Objects.requireNonNull(result.collectList().block()));
  • 尝试展示端点实现.. 至少重要的部分
  • 我添加了 REST-Endpoint

标签: java unit-testing mocking mockito spring-webflux


【解决方案1】:

实际对象@RequestBody Mono&lt;ItemParams&gt; 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&lt;ItemParams&gt; 传递到函数中。这就是为什么我创建一个并将其放入我的Mockito.when 并放入我的BodyInserter。这意味着它肯定是同一个对象。
  • 所以你只想确保传递了一个 Mono 类型的对象?不管它的内容如何?
  • 没有。不管它的内容如何,​​这就是我使用Mono&lt;ItemParams&gt;而不是Mono的原因。 Mono 的内容必须是 ItemParams 类型的
  • 不完全确定该类的 api / equals 方法是什么,但通常使用 thenAnswer 功能解决基于传递对象内容的结果。使用 when.. then 的正常对象期望在这里似乎不起作用,因此出现异常
  • 无论如何。您的解决方案正在努力强制测试只接受 Mono&lt;ItemParams&gt; 作为输入参数。