问题
下述利用MockMvc做单测的代码,运行时usermapper会出现NPE
public class HelloController {
@Autowired
private UserMapper userMapper;
@RequestMapping("/user")
public User getUser() {
return userMapper.selectOne(1);
}
}
@SpringBootTest
public class HelloControllerTest {
private MockMvc mockMvc;
@BeforeEach
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(new HelloController()).build();
// 改成下述代码不会出现NPE
// mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
}
@Test
public void testGetUser() {
mockMvc.perform(MockMvcRequestBuilders.get("/user"))
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
}
解析
留意MockMvcRequestBuilders有两个构造方法,standaloneSetup是用来注册@Controller和Spring MVC底层组件的,不会注册其他用户自定义的Component,所以usermapper会出现NPE.
而webAppContextSetup会初始化整个context,也当然也包括用户注册的Component,因此不会出现NPE
其他写法
总觉得上述写法不太优雅,如setup()被执行多次等,Spring提供了另外一种写法
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testGetUser() {
mockMvc.perform(MockMvcRequestBuilders.get("/user"))
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
}
注意这里多了@AutoConfigureMockMvc,他会导入MockMvcAutoConfiguration,自动装配出MockMvcBuilder,进而得到MockMvc
public class MockMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean(MockMvcBuilder.class)
public DefaultMockMvcBuilder mockMvcBuilder(List<MockMvcBuilderCustomizer> customizers) {
// 全初始化context
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.context);
builder.addDispatcherServletCustomizer(new MockMvcDispatcherServletCustomizer(this.webMvcProperties));
for (MockMvcBuilderCustomizer customizer : customizers) {
customizer.customize(builder);
}
return builder;
}
@Bean
@ConditionalOnMissingBean
public MockMvc mockMvc(MockMvcBuilder builder) {
// 构造出MockMvc,后面使用注入即可
return builder.build();
}
// 其余省略...
}
@WebMvcTest和@SpringBootTest有什么不同呢
@SpringBootTest里面包含了
- @BootstrapWith(SpringBootTestContextBootstrapper.class),用于查找启动装配类(@SpringBootConfiguration)
- @ExtendWith(SpringExtension.class),是spring-test和junit5的集成
@WebMvcTest同样包含上述两个功能,除此之外还有: - @TypeExcludeFilters(WebMvcTypeExcludeFilter.class), 只会注册
@Controller, @ControllerAdvice, @JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer, HandlerMethodArgumentResolver, 不包括@Component, @Service @Repository - @AutoConfigureCache, 启用缓存
- @AutoConfigureWebMvc, 启用WebMvc
- @AutoConfigureMockMvc, 启用MockMvc
可以看出@WebMvcTest基本包含了@SpringBootTest的功能,但前者去掉了@Component, @Service, @Repository,可以认为只针对Controller测试,更加轻量,这是两者的不同之处.