上面我们提到@AutoConfigureMockMvc 提供了自动配置 MockMvc 的功能,实例化MockMvc 的
具 体 代 码 在
spring-boot-test-autoconfigure 项 目 中 的MockMvcAutoConfiguration 自 动 配 置 类 内 。 而 该 自 动 配 置 类 的 生 效 又 涉 及 了@AutoConfigureMockMvc 注解。本节我们就大致来了解一下@AutoConfigureMockMvc 和MockMvcAutoConfiguration。
上节的例子中使用@AutoConfigureMockMvc 注解来引入启动单元测试的自动注入,从而注入 MockMvc 类的 Bean。那么,@AutoConfigureMockMvc 只是注入了 MockMvc 的 Bean吗?并不是的,我们来看一下
@AutoConfigureMockMvc 的源代码。
@Target({ ElementType . TYPE,ElementType .METHOD })
@Retent ion(RetentionPolicy . RUNTIME)@Documented
@Inherited
@ImportAutoConfiguration
@PropertyMapping(" spring. test . mockmvc")
public @interface AutoConfigureMockMvc {
//是否应向 MockMVC 注册来自应用程序上下文的 filter,默认为 true
boolean addFilters() default true;
// 每次 MockMVC 调用后应如何打 EMvcResult 信息
@PropertyMapping(skip = SkipPropertyMapping .ON DEFAULT _VALUE)
MockMvcPrint print() default MockMvcPrint . DEFAULT;
//如果 MvcResult 仅在测试失败时才打印信息。true,则表示只在失败时打印
boolean printOnlyOnFailure() default true;
/当 HtmUnit 在类路径上时, 是否应该自动配置 webCliento 默认为 true
@PropertyMapping( "webclient . enabled")
boolean webClientEnabled() default true;
//当 Selenium 位于类路径上时,是否应自动配置 WebDriver。默认 为 true
@PropertyMapping("webdriver . enabled")
boolean webDriverEnabled() default true;
}
AutoConfigureMockMvc 中定义的属性比较简单,除了 print 属性是用于配置每次 MockMVC调用后打印 MvcResult 信息之外,其余的配置均为设置特定情况下是否进行相应处理。可结合上述代码中的注释部分了解对应属性的详细功能。同时,在上节的实例中(也是通常情况下)我们并没有进行特殊的配置,都采用该注解中的默认值。
在 AutoConfigureMockMvc 的源码中,我们重点看它组合的@ImportAutoConfiguration 注解 。 该 注 解 同 样 是 Spring Boot 自 动 配 置 项 目 提 供 的 , 其 功 能 类 似@EnableAutoCon-figuration,但又略有区别。
@lmportAutoConfiguration 同样用于导入自动配置类,不仅可以像@EnableAutoConfiguration 那样排除指定的自动配置类,还可以指定使用哪些自动配置类,这是它们之间的重要区别之一。
另外,@ImportAutoConfiguration 使用的排序规则与
@EnableAutoConfiguration 的相同,通常情况下,建议优先使用
@EnableAutoConfiguration 注解进行自动配置。但在单元测试中,则可考虑优先使用
@lmportAutoConfiguration。下面看 一下它的源码及功能,代码如下。
@Target(ElementType . TYPE)
@Retention( RetentionPolicy . RUNTIME)
@Documented@Inherited
@Import( ImportAutoConfigurationImportSelector. class)
public @interface ImportAutoConfiguration {
//指定引入的自动配置类
@AliasFor("classes")
Class[] value() default {};
//指定引入的自动配置类。如果为空,则使用 ME TA- INF/spring. factories 中注册的指定类
//其中 spring. factories 中注册的 key 为被该注解的类的全限定名称
@AliasFor("value")
Class[] classes() default {};
/排除指定自动配置类
Class[] exclude() default {};
}
上述代码中关于 ImportAutoConfiguration 导入的 Selector 与之前讲解@EnableAuto-Configuration 时的使用流程基本一致,我们不再赘述。
下面来看 ImportAutoConfiguration 中定义的属性。通过 value 属性,提供了指定自动配置类 的 功 能 , 可 以 通 过 细 粒 度 控 制 , 根 据 需 要 引 | 入 相 应 功 能 的 自 动 配 置 。 没 有@EnableAutoConfiguration-次注入全局生效的特性,但是有了指定的灵活性。
更值得注意的是 classes 属性,它也是用来指定自动配置类的,但它的特殊之处在于,如果未进行指定,则会默认搜索项目 ME TA-INF/spring.factories 文件中注册的类,但是它搜索的注册类在 spring.factories 中的 key 是被@ImportAutoConfiguration 注解的类的全限定名称。显然,这里的 key 为 org.springframework.
boot.test.autoconfigure.web.servlet.Auto-ConfigureMockMvc 。 以 上 功 能 也 就 解 释 了 为 什 么 在 单 元 测 试 中 更 多 的 是 使 用@lmportAuto-Configuration 注解来进行自动配置了。
在
spring-boot-test-autoconfigure 项目的 spring.factories 文件中的相关配置如下。
# AutoConfigureMockMvc auto-configuration imports
org. springframework . boot . test . autoconfigure . web. servlet . AutoConf igureMockMv
org. springframework. boot . test . autoconfigure . web . servlet . MockMvcAutoConfigur
ation, \
org. springframework . boot . test . autoconfigure . web . servlet . MockMvcWebClientAut
oCon-
figuration, \
org . springframework. boot . test. autoconfigure . web . servlet. MockMvcWebDriverAut
oCon-
figuration, \
org. springframework . boot . autoconfigure . security. servlet. SecurityAutoConfiguration,\
org. springframework . boot . autoconfigure . security . servlet . UserDetailsServiceA
uto-
Configuration, \
org. springframework. boot . test . autoconfigure . web . servlet . MockMvcSecurity
Configuration
也就是说,当使用@lmportAutoConfiguration 注解,并未指定 classes 属性值时,默认自动配置上述自动配置类。
关 于 @ImportAutoConfiguration 我 们 就 讲 这 么 多 , 读 者 朋 友 可 以 对 照@EnableAuto-Configuration 相关章节中提供的方法,当作练习自行阅读该注解导入的
ImportAutoConfi-gurationlmportSelector。下节我们以配置中的 MockMvcAutoConfiguration为例,讲解 MockMvc 相关的自动化配置。
上 一 节 我 们 知 道 通 过 使 用 @AutoConfigureMockMvc 注 解 会 导 入MockMvcAutoCon-figuration 自动配置类,该类就是专门为 MockMvc 相关功能提供自动配置的。
先看 MockMvcAutoConfiguration 的注解和构造方法部分源代码。
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type . SERVLET)
@AutoConfigureAfter (WebMvcAutoConfiguration. class)
@EnableConfigurationProperties({ ServerProperties. class, WebMvcProperties.c
lass } )
public class MockMvcAutoConfiguration {
private final WebApplicationContext context;
private final WebMvcProperties webMvcProperties;
MockMvcAutoConfiguration(WebApplicationContext context, WebMvcProperties
webMvcProperties) {
this. context = context;
this
. webM
Properties = webMvcProperties; }}
注解部分说明,MockMvcAutoConfiguration 需 要在 Web 应用程序类型为 Servlet,且在WebMvcAutoConfiguration 自 动配置之后进行自动配置。关于 WebMvcAutoConfiguration我们在前面章节已经讲到,这里不再赘述。另 外 , 通 过 @
EnableConfigurationProperties 导 入 了 ServerProperties 和WebMvcProperties 两个配置属性类,并通过构造方法设置为成员变量。
下面挑选 MockMvcAutoConfiguration 中几个比较重要的自动配置 Bean 来进行讲解,首先看 DispatcherServletPath 的注入。
@Bean
@Condit ional0nMi ssingBean
public DispatcherServletPath dispatcherServletPath() {
return () -> this . webMvcProperties . getServlet() . getPath();}
当容器中不存在 DispatcherServletPath 的 Bean 时,会创建一个 DispatcherServletPath 实现 类 的 对 象 , 并 注 入 容 器 。 其 中 DispatcherServletPath 是 一 个 接 口 , 用 来 提 供DispatcherServlet 所需路径的详细信息。在上述代码中实现了 DispatcherServletPath 的getPath 方法,并返回 WebMvcProperties 配置:文件默认(“") 或指定的 Web 访问路径。
MockMvc 主 要 是 通 过 MockMvcBuilder 创 建 的 , 默 认 情 况 下 实 例 化 了DefaultMock-MvcBuilder,相关代码如下。
@Bean
@Conditional0nMi ssingBean(MockMvcBuilder . class)
public DefaultMockMvcBuilder mockMvcBuilder(List
customizers) {
DefaultMockMvcBuilder builder = MockMvcBuilders . webAppContextSetup(this. (
ontext) ;
builder . addDispatcherServletCustomizer (new MockMvcDi spatcherServletCusto-
mizer(this . webMvcProperties));
for (MockMvcBuilderCustomizer customizer : customizers) {
customizer. customize(builder);}
return builder;}
当 容 器 中 不 存 在 MockMvcBuilder 的 Bean 时 , 通 过 MockMvcBuilders 的webAppContextSetup 方法创建 DefaultMockMvcBuilder ,然后设置 DispatcherServlet 和MockMvcBuilder 的 定 制 化 配 置 。 其 中 , 关 于 DispatcherServlet 的 设 置 就 在MockMvcAutoConfiguration 中定义,其核心代码如下。
@Override
public void customize(DispatcherServlet dispatcherServlet) {
dispatcherServlet . setDispatchOptionsRequest(this . webMvcProperties . isDispa
tch-
Opt ionsRequest());
dispatcherServlet . setDi spatchTraceRequest(this . webMvcProperties . isDispa-tchTraceRequest());
dispatcherServlet
. setThrowExceptionIfNoHandlerFound( this . webMvcProperties . isThrowExcep-
tionIfNoHandlerFound());
}
上述代码就是对配置文件 WebMvcProperties 中 DispatcherServlet 是 否 分 发“HTTPOPTIONS"请求、是否分发“HTTPTRACE"、是否抛出 NoHandlerFoundException进行配置。
当获得了 MockMvcBuilder,便可以配置并实例化 MockMvc 了,相关代码如下。
@Bean
@ConditionalOnMi ssingBean
public MockMvc mockMvc (MockMvcBuilder builder) {
return builder . build(); }
至此,MockMvc 已经被实例化并注入容器了。 当然,如果容器中不存在 DispatcherServlet对应的 Bean, 也会进行相应的自动配置。
@Bean
@ConditionalOnMissingBean
public
Di spatcherServlet dispatcherServlet (MockMvc mockMvc) {
return mockMvc . getDispatcherServlet();
}
这里是通过 MockMvc 提供的方法来获得 DispatcherServlet 的 Bean,并注册。
正是有了上述自动配置机制,我们在单元测试时直接在单元测试类上使用@AutoCon-figureMockMvc 注解之后,便可以直接通过@Autowired 对 MockMvc 进行注入并使用了。
本章简单地介绍了 Spring Boot 中对单元测试的支持,以及常用的注解、单元测试实例。关于单元测试开启及自动注入我们讲解了@AutoConfigureMockMvc。 类似的,Spring Boot还提供了许多更加有针对性、使用快捷的注解,比如:针对 JSON 的@JsonTest、 针对 MVC的@WebMvcTest、针对 WebFlux 的@WebFluxTest、 针对 Data JPA 的@DataJpaTest 、针 对 JDBC 的 @JdbcTest 针 对 MongoDB 的 @DataMongoTest 、 针 对 redis 的@DataRedisTest 等 。 但 如 果 我 们 阅 读 上 述 注 解 的 源 码 , 会 发 现 其 处 理 机 制 与@AutoConfigureMockMvc 基 本一致 , 核 心 部分都使用了本章讲到的@ImportAutoConfiguration 注解。
本章的重点并不仅仅是要教会大家如何使用单元测试,更重要的是传达个思想:单元测试是保证代码质量的重要方式,在具体项目中,如果有可能,请尽量编写单元测试代码。