MockMvc 是否有资格进行 WebFlux 控制器测试?

Is MockMvc eligible for WebFlux controllers testing?

我有一个简单的 WebFlux 应用程序(使用控制器,而不是路由器功能)。唯一的非标准部分是它使用 Server-Sent-Events。

控制器的一个有趣的部分是

    @GetMapping(path = "/persons", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<Object>> persons() {
        return service.persons()
                .map(this::personToSse)
                .onErrorResume(e -> Mono.just(throwableToSse(e)));
    }

    private ServerSentEvent<Object> personToSse(Person person) {
        return ServerSentEvent.builder().data(person).build();
    }

服务:

public interface Service {
    Flux<Person> persons();
}

我有两个测试:

@SpringBootTest(classes = Config.class)
@AutoConfigureMockMvc
class PersonsControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private Service service;

    @Test
    void streamsPersons() throws Exception {
        when(service.persons())
                .thenReturn(Flux.just(new Person("John", "Smith"), new Person("Jane", "Doe")));

        String responseText = mockMvc.perform(get("/persons").accept(MediaType.TEXT_EVENT_STREAM))
                .andExpect(status().is2xxSuccessful())
                .andExpect(content().string(not(isEmptyString())))
                .andReturn()
                .getResponse()
                .getContentAsString();

        assertThatJohnAndJaneAreReturned(responseText);
    }

    @Test
    void handlesExceptionDuringStreaming() throws Exception {
        when(service.persons())
                .thenReturn(Flux.error(new RuntimeException("Oops!")));

        String responseText = mockMvc.perform(get("/persons").accept(MediaType.TEXT_EVENT_STREAM))
                .andExpect(status().is2xxSuccessful())
                .andReturn()
                .getResponse()
                .getContentAsString();

        assertThat(responseText, is("event:internal-error\ndata:Oops!\n\n"));
    }

第一个测试检查 'sunny day scenario' 我们得到了我们期望的两个人。第二个测试检查发生异常时会发生什么。

当我 运行 一项一项地进行测试时,测试工作完美。但是当我 运行 他们两个时,有时他们通过,有时其中一个失败,有时两个都失败。失败原因不同:

  1. 有时 Jackson 在 JSON 解析期间抱怨已达到 EOF('No content to map due to end-of-input',尽管在日志中我可以看到有效的完整 JSON)
  2. 有时第一次测试失败而第二次通过,就好像在这两种情况下都返回了错误,即使我可以在日志中看到第一次测试生成了正常响应,而不是错误的响应
  3. 有时第二次测试失败,第一次通过,就好像在这两种情况下都有效 JSONs where returned

看起来好像存在一些并发问题。但是我的测试代码很简单,没有用到任何并发相关的概念。

以下测试在我的机器上 100% 次失败(只是 运行 这两个测试重复 1000 次):

    @Test
    void manyTimes() throws Exception {
        for (int i = 0; i < 1000; i++) {
            streamsPersons();
            handlesExceptionDuringStreaming();
        }
    }

问题如下:

  1. MockMvc 可以用来测试反应式控制器吗?
  2. 如果可以,我是不是做错了什么?

这里是完整的项目源代码:https://github.com/rpuch/sse-webflux-tests manyTests() 方法被注释掉,必须重新启用才能使用。

1. MockMvc 是否可以用于测试反应式控制器?

答案是否定的,MockMvc 是一个阻塞的 mockClient,它将调用您的方法一次 return。它无法在项目发出时连续读取项目您需要使用的客户端是 Spring WebClient.

您可以在此处阅读更多有关如何使用 Spring WebTestClient.

testing infinite streams 的信息

2。如果可以,我是不是做错了什么?

查看问题一的答案。