junit MockRestServiceServer看到来自先前测试的请求

kjthegm6  于 2023-04-06  发布在  其他
关注(0)|答案(1)|浏览(134)

我使用MockRestServiceServer运行JUnit5,发现当我使用异步请求运行多个测试时,测试A中的MockRestServiceServer将看到来自测试B的请求并标记为unexpected。

@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
public class MyTestClass {

  @Autowired
  private RestTemplate restTemplate;
  @Autowired
  private WebApplicationContext wac;

  private MockRestServiceServer mockServer;
  private MockMvc mockMvc;

  private URI asyncUri = URI.create("/asyncUrl");

  @BeforeEach
  public void setUp(){
    mockerServer = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();
    mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
  }

  @AfterEach
  public void tearDown(){
    mockServer.verify();
    mockServer.reset();
  }

  @Test
  public void dontRunAsyncMethod(){
    mockServer.reset(); // Added trying to fix the issue
    mockServer.expect(ExpectedCount.never(), requestTo(asyncUri));

    URI uri = URI.create("/myApi?runAsyncMethod=false");
    mockMvc.perform(get(uri));
  }

  @Test
  public void doRunAsyncMethod(){
    mockServer.reset(); // Added trying to fix the issue

    // Side issue here - I have to use `between` rather than `times`
    // because mockServer validates before request happens
    mockServer.expect(ExpectedCount.between(0, 1), requestTo(asyncUri));

    URI uri = URI.create("/myApi?runAsyncMethod=true");
    mockMvc.perform(get(uri));
  }
}

当单独运行时,两个测试都通过了。但是,当我一起运行它们时,dontRunAsyncMethod将失败,说没有请求被期望到/asyncUrl。有没有一种方法可以更好地分离测试?我可以以某种方式确保在开始下一个测试之前所有请求都已完成?

xqk2d5yq

xqk2d5yq1#

你的评论本身提供了一个提示:

// Side issue here - I have to use `between` rather than `times`
// because mockServer validates before request happens

这实际上是个问题测试不会等待后台的异步调用。
首先,这个Assert应该被纠正为times(1),因为它不会纠正异步调用在这个测试中被执行0次。通过允许它Assert调用可以被执行0次,异步行为的测试只是通过没有指定正确的行为。
现在,这个测试在调用之前完成的事实(并且将在正确Asserttimes(1)时失败)与以下事实直接相关:如果两个测试都运行,则另一个测试无法Assert异步调用 not made。两者的原因都是异步测试没有等待异步调用异步发生:因此,调用在某个时刻在后台触发,但要等到一个测试完成并且JUnit转移到另一个测试。
解决这个问题的方法应该是确保测试等待异步处理。希望类似这样的东西可以工作(假设/myApi实现中的异步机制与这里讨论的相同):

@Test
  public void doRunAsyncMethod(){
    mockServer.expect(ExpectedCount.times(1), requestTo(asyncUri));

    URI uri = URI.create("/myApi?runAsyncMethod=true");
    MvcResult mvcCall = mockMvc.perform(get(uri))
            .andExpect(status().isOk()) 
            .andExpect(request().asyncStarted()) 
            .andExpect(request().asyncResult("expected response here")) 
            .andReturn();
    mockMvc.perform(asyncDispatch(mvcCall)) 
            .andExpect(status().isOk()) 
            .andExpect(content().string("expected response here"));
  }

(You我必须调整响应主体,如果响应状态不OK,则删除这些行或将其更改为预期状态。从文档中我不能立即清楚这些预期集合中的哪一组是指您正在测试的对/myApi的调用的响应,它引用了来自mockServer.expect(ExpectedCount.times(1), requestTo(asyncUri));的响应-我在问题中没有看到/myApi的实现,所以我在这里能做的最好的事情就是模仿文档。你也许可以查找asyncResult的JavaDocs,看看是否有一个版本的响应体为空/没有响应体,如果你的回复根本就不应该有内容。)
另外,如果您的 * nonasync * 测试导致进行异步调用,但是因为它没有等待最初不应该发生的异步进程,所以测试仍然通过了never调用的Assert,该怎么办?(并不是真正意义上的“永远”,而是在测试运行的时间内计数0次调用!)为了避免这种情况,我们还需要Assert该测试的请求没有运行异步活动:

@Test
  public void dontRunAsyncMethod(){
    mockServer.expect(ExpectedCount.never(), requestTo(asyncUri));

    URI uri = URI.create("/myApi?runAsyncMethod=false");
    mockMvc.perform(get(uri))
            .andExpect(request().asyncNotStarted());
  }

顺便说一句,你可以在每个测试开始时删除这些行(@AfterEach中的verifyreset不需要这些行):

// remove this:
    mockServer.reset(); // Added trying to fix the issue

相关问题