问题
下述利用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测试,更加轻量,这是两者的不同之处.