解析“@ExtendWith注解“

背景:

学习Spring中, 发现在Junit5下Spring测试case上会添加@ExtendWith(SpringExtension.class) 该注解的掌握与使用对于日常开发中的写单测很有帮助.

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { BirdConfig.class, CatConfig.class, DogConfig.class })
class ConfigUnitTest {

    @Autowired
    ApplicationContext context;

    @Test
    void givenImportedBeans_whenGettingEach_shallFindIt() {
        assertThatBeanExists("dog", Dog.class);
        assertThatBeanExists("cat", Cat.class);
        assertThatBeanExists("bird", Bird.class);
    }

    private void assertThatBeanExists(String beanName, Class beanClass) {
        assertTrue(context.containsBean(beanName));
        assertNotNull(context.getBean(beanClass));
    }
}

作用原理:

@ExtendWith(SpringExtension.class)提供了Spring单测的上下文环境, 会启动一个用于单测的spring容器, 完成单测中所需的bean对象的构建与自动注入. 而其实现上则利用了则利用了: Junit5提供的@ExtendWith扩展能力 + Spring单测需要的SpringExtension.class

@ExtendWith:

@ExtendWith是Junit5新引入的注解, 用于扩展test能力, 通过提供一系列的扩展点(extension point)来支持用户在执行具体的单测实例前后去做一些环境准备等工作, 这部分工作与单测内容无关, 但对于单测的正常执行却至关重要, 比如Spring单测需要启动一个spring容器, 完成指定bean的构建; 或执行测试数据库前进行连接数据库等工作.

如果将每个单测实例比成一个关注点(joint point), 则该思想类似于AOP, 通过@ExtendWith导入指定的Extention类所定义的功能扩展点,

对于单测实例来说常见的扩展点主要包含:

  • 单测是否执行判断
    实现ExecutionCondition接口, 根据参数判断该单测是否进行执行, 比如根据配置文件application.properties的环境参数env==test 指定单测执行的前提条件.
  • 单测实例生命周期运行中的回调函数
    类似于AOP中的Advice, 是对单测实例执行前后所插入的扩展点逻辑, 可以实现各种类型的扩展接口, 比如若需要在单测实例构建前后插入扩展逻辑, 则可以实现BeforeAllCallback 和 AfterAllCallback接口;需要在所有的测试方法执行前后所插入的测试逻辑, 则通过实现BeforeEachCallBack 和 AfterEachCallback接口; 若需要在执行单个测试方法前后要插入的扩展逻辑, 则可以实现BeforeTestExecutionCallback 和 AfterTestExecutionCallback, 因此具体的一个单测方法整个生命周期的回调函数执行顺序如下所示:
      1. BeforeAllCallback
      2. BeforeAll
      3. BeforeEachCallback
      4. BeforeEach
      5. BeforeTestExecutionCallback
      6. Test
      7. AfterTestExecutionCallback
      8. AfterEach
      9. AfterEachCallback
      10. AfterAll
      11. AfterAllCallback

通过实现并注册这些扩展点所定义的接口到Junit5中, 完成更复杂场景单测实例的构建.

  • 测试实例后处理: 实现TestInstancePostProcessor接口, 用于当测试实例构建完成后进行后处理(测试实例就是单元测试类的实例, 不是每个标注@test注解的单测方法).
  • 参数解析: 实现ParameterResolver, 虽然常见构造的测试实例(test instance)是无参构造函数, 但有时需要在运行单测指定参数, 则需要ParameterResolver来将参数转换成测试实例所需要的构造参数类型.

SpringExtension.class

SpringExtension是对上述功能扩展点的一种实现, 用来将Spring Test Framework 集成到Junit5测试环境中, 其类定义实现了生命周期回调/测试实例后处理/参数解析等扩展点的接口, 具体类定义如下:

public class SpringExtension implements BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor,
    BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback,
    ParameterResolver {
    ...
}

通过这些扩展点, 完成Spring的TestContext构建与初始化, 并将指定的类的bean的构建.

参考博客:

  • A Guide to JUnit 5 Extensionshttps:
  • java-spring-mockito-mock-mockbean
  • @InjectMocks

  • Junit-springrunner-vs-mockitojunitrunner
  • @ContextConfiguration
  • @Configuration

  • Migrating from JUnit 4 to JUnit 5

你可能感兴趣的:(Spring学习笔记,junit,java,spring)