Is there a way to enable virtual threads when executing through @SpringBootTest? I've tried both known ways, but nothing works with @SpringBootTest. 1: spring.threads.virtual.enabled=true 2: with beans
Here are my tests:
- my controller:
@GetMapping("/api")
protected ResponseEntity<?> runningOnVirtualThread() {
System.out.println(Thread.currentThread());
return ResponseEntity
.ok().body(Thread.currentThread().isVirtual());
}
- tests
@Autowired
protected MockMvc mockMvc;
@Test
void testing_with_MockMvc() throws Exception {//DO NOT PASS
var response = mockMvc.perform(MockMvcRequestBuilders.get("/api")).andExpect(status().isOk()).andReturn();
//controller returns false, so the next fails.
Assertions.assertEquals("true",response.getResponse().getContentAsString());
}
@Test
void testing_with_RestAssured() {//PASS
DemoApplication.main(new String[]{});
RestAssured.given().when().get("http://localhost:8080/api").then().statusCode(200).and().body(equalTo("true"));
}
To begin with, testing your endpoint with
RestAssured-powered full-blown HTTP request to it and with Mock Mvc technique are two completely different things from the standpoint of the execution of this endpoint.RestAssured, or just plainTestRestTemplate, issues an HTTP request that does hit the (embedded) Tomcat server and whatever you configured for Tomcat Connector, protocol, with Spring Boot propertyspring.threads.virtual.enabledetc gets invoked and comes into play.In contrary, Mock Mvc technique completely bypasses the Tomcat Connectors and other Tomcat machineries, instead handing the HTTP request directly to Spring
TestDispatcherServlet.Therefore, with Mock Mvc, your Controller method executes on the same thread as your test executes, while with REST techniques this method executes on Tomcat Connector-controlled thread.
Therefore, the situation you stumbled across when with Mock Mvc the Controller method is not executed on virtual thread is absolutely normal and I wouldn't bother about it at all.
Although I see little to no value whatsoever in "fixing" this issue and in forcing Mock Mvc to run on a virtual thread, it is difficult to envision all the ramifications you have with your tests, and, if it is absolutely necessary, then I could offer a very simple walkaround - to run your Mock Mvc test already on a virtual thread.
Technically, it might look like a drop-in replacement of
MockMvcclass, let's call itMockMvcOnVirtualThreads; you can pick up less awkward name.It then could be instantiated like this:
and then, as a drop-in, you just inject in your tests this
MockMvcOnVirtualThreadsinstance instead of original, genuineMockMvc:(Don't forget to let Spring Boot Test engine to pick up
MockMvcConfigurer)As you can see, the above differs very little from plain execution of your test on a virtual thread like this:
, just very basic wrapping.
If everything above makes at least some sense to you, it would be easy for me to publish fully working POC, please let me know.
Few additional in-depth notes:
It is easy to see that
MockMvc,TestDispatcherServletand others up-stack of your tests ignore Spring Boot propertyspring.threads.virtual.enabled, while they could honor it. Whether Spring men would do in the future and honor virtual thread setting in MockMvc is difficult for me to say.Invocation on a virtual thread with Mock Mvc could be done upper-in-stack on other Spring layers:
TestDispatcherServlet/DispatcherServlet,HandlerAdapter, (moreoverRequestMappingHandlerAdapterhas settableTaskExecutor), interceptors and so on. At this point of time I cannot speak of advantages or disadvantages of such tweaking.MockMvc,TestDispatcherServlet, which is just a subclass ofDispatcherServlet, also supports asynchronous requests. I was not able to effectively use this technique for the purpose of executing on a virtual thread and cannot say at the moment might this be a right way to go.