I am running JUnit5 with MockRestServiceServer and seeing that when I run multiple tests with async requests, the MockRestServiceServer in test A will see a request from test B and flag as 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));
}
}
When run separately, both tests pass. However, when I run them together, dontRunAsyncMethod will fail saying that no request was expected to /asyncUrl. Is there a way to separate the tests more so? Can I somehow ensure that all requests have completed before beginning the next test?
Your comment itself provides a hint:
That's actually the issue; that the test doesn't wait for the asynchronous call made in the background.
First off, this assertion should be corrected to
times(1)because it wouldn't be correct for the async call to be made 0 times in this test. By allowing it to assert that the call could be made 0 times, the async behavior's test is only passing by not specifying the correct behavior.Now, the fact that this test completes before the call is made (and will fail with a correct assertion of
times(1)) is directly related to the fact that if both tests are run, the other test is failing its assertion that the async call is not made. The cause of both is that the async test is not waiting for the async call to, well, to happen asynchronously: so the call fires off in the background at some point, but not till the one test has completed and JUnit has moved on to the other test.The fix for this should be to ensure the test waits for the asynchronous processing. Hopefully something like this works (assuming the asynchronous mechanism in the implementation of
/myApiis the same one that is talked about here):(You'll have to tweak the response bodies, and if your response status isn't OK then remove those lines or change them to the expected status. It's not immediately clear to me from the documentation which of these sets of expectations refers to the response from the call to
/myApithat you are testing, and which refers to the response frommockServer.expect(ExpectedCount.times(1), requestTo(asyncUri));– and I don't see the implementation of/myApiin the question, so the best I can do here is imitate the documentation. You might be able to look up the JavaDocs forasyncResultand see if there is a version with an empty/no response body, if your response isn't supposed to have content at all.)Also, what if your non-async test causes the async call to be made, but because it doesn't wait for the async process that isn't supposed to happen in the first place, the test still passes the assertion that it is
nevercalled? (Not really "never" in the sense of for all time, but rather a count of 0 calls in the time the test is run!) To avoid this, we need to also assert that no async activity is run by that test's request:As an aside, you can remove the lines at the start of each test (which are not needed with the
verifyandresetin the@AfterEach):