I discovered that I have to manually @Import
autoconfiguration classes when using @WebMvcTest
:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(controllers = MyWebMvcTest.ComplimentingConfig.class)
@Import({GatewayServerMvcAutoConfiguration.class,
SslAutoConfiguration.class,
RestClientAutoConfiguration.class})
public class MyWebMvcTest {
@Autowired
MockMvc mockMvc;
@Test
void test() throws Exception {
mockMvc.perform(get("/get"))
.andExpect(status().isOk());
}
@Configuration
static class ComplimentingConfig {
@Bean
public RouterFunction<ServerResponse> getRoute() {
return route()
.GET("/get", http("https://httpbin.org/status/200"))
.build();
}
}
}
If I decide to import an @EnableAutoConfiguration
-annotated configuration and thus include those imports implicitly, I get an exception as some required beans are not present in the context
@WebMvcTest(controllers = MyWebMvcTest.ComplimentingConfig.class)
@Import(MyWebMvcTest.ComplimentingConfig.class)
public class MyWebMvcTest {
// ...
@Configuration
@EnableAutoConfiguration
static class ComplimentingConfig {
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.gateway.server.mvc.handler.ProxyExchangeHandlerFunction' available
Why doesn't @EnableAutoConfiguration
work with @WebMvcTest
?
Java 17, Spring MVC 6.1.5, Spring Boot 3.2.4
Note. I used Spring Cloud Gateway MVC in the MRE above to necessitate additional @Import
s. Had I used a plain RouterFuction
(e.g. route().GET("/get", r -> ServerResponse.ok().build()).build()
), the whole point would've been moot since all required beans would be implicitly provided by @WebMvcTest
First you need to understand between slice testing and integration testing.
@WebMvcTest
does not start full Spring application context. It is used for slice testing of web layer only. It scans for classes annotated with @Controller
. To narrow down you can mention controller name like @WebMvcTest(XyzController.class)
. It is often accompanied with @MockBean
in case you controller has reference to another class. It will not read your other configurations. Similarly, @DataJpaTest
loads @Repository
classes.
Now you left with two options:
@SpringBootTest
- will look for main configuration class (one you annotated with @SpringBootApplication
) and start Spring application context. This you can use for your integration testing. Here you defined either you want to use server or not
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class TestClass {}
This will also start you server. If you don't want that you can use
@SpringBootTest
@AutoConfigureMockMvc
class TestClass {}
@ContextConfiguration(classes = {XyzConfig.class})
- will load only mentioned configurations. This will be your slice testing.
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {XyzConfig.class})
class TestClass {}
Now you can decide how you want to proceed.
Why doesn't @EnableAutoConfiguration work with @WebMvcTest ?
@WebMvcTest
annotation will disable full auto-configuration and apply only configuration relevant to MVC tests, like I explained above. Even if you have explicitly mentioned auto-configuration, @WebMvcTest
is overriding it. Read this