暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Junit5 源码分析系列(六)

测开技术笔记 2021-05-09
424

       我们先看下ClassTestDescriptor所构成的NodeTestTask的执行流程,执行before方法,跳转到父类ClassBasedTestDescriptor 里面执行

public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) {
    //查找新的Extension, 这里才注册SpringExtension
MutableExtensionRegistry registry = populateNewExtensionRegistryFromExtendWithAnnotation(
context.getExtensionRegistry(), this.testClass);


// Register extensions from static fields here, at the class level but
    // after extensions registered via @ExtendWith.
    //查找filed 域的扩展,如被注解 @RegisterExtension 修饰的属性
registerExtensionsFromFields(registry, this.testClass, null);


// Resolve the TestInstanceFactory at the class level in order to fail
// the entire class in case of configuration errors (e.g., more than
// one factory registered per class).
    //当前为null, 跳过
this.testInstanceFactory = resolveTestInstanceFactory(registry);


    //注册 被注解BeforeEach 修饰的方法,在我们当前举得例子中,是基类BaseTest中的init方
    //这里注册的是 BeforeEachMethodAdapter Extensio
registerBeforeEachMethodAdapters(registry);
    //注册 被注解AfterEach 修饰的方法
registerAfterEachMethodAdapters(registry);


ThrowableCollector throwableCollector = createThrowableCollector();
ClassExtensionContext extensionContext = new ClassExtensionContext(context.getExtensionContext(),
context.getExecutionListener(), this, this.lifecycle, context.getConfiguration(), throwableCollector);
    //通过注解查找beforeAll方法
this.beforeAllMethods = findBeforeAllMethods(this.testClass, this.lifecycle == Lifecycle.PER_METHOD);
    //通过注解查找afterAll方法
this.afterAllMethods = findAfterAllMethods(this.testClass, this.lifecycle == Lifecycle.PER_METHOD);


    //刷新context
return context.extend()
         //这里是个关键流程,注册了TestInstancesProvider,为后面初始化testInstance做准备
.withTestInstancesProvider(testInstancesProvider(context, extensionContext))
.withExtensionRegistry(registry)
.withExtensionContext(extensionContext)
.withThrowableCollector(throwableCollector)
.build();
// @formatter:on
}

  //lambda初始化TestInstancesProvider接口实现类
private TestInstancesProvider testInstancesProvider(JupiterEngineExecutionContext parentExecutionContext,
ClassExtensionContext extensionContext) {


    return (registry, registrar) -> extensionContext.getTestInstances().orElseGet(
() -> instantiateAndPostProcessTestInstance(parentExecutionContext, extensionContext, registry, registrar));
}

private TestInstances instantiateAndPostProcessTestInstance(JupiterEngineExecutionContext parentExecutionContext,
      ExtensionContext extensionContext, ExtensionRegistry registry, ExtensionRegistrar registrar) {
      ......
}

执行完prepare方法后,在executeRecursively方法中继续执行node.before方法

public JupiterEngineExecutionContext before(JupiterEngineExecutionContext context) {
ThrowableCollector throwableCollector = context.getThrowableCollector();
    ......
if (throwableCollector.isEmpty()) {
context.beforeAllCallbacksExecuted(true);
      //执行beforeAllCallBack方法,在这里初始化TestContextManager
invokeBeforeAllCallbacks(context);


if (throwableCollector.isEmpty()) {
context.beforeAllMethodsExecuted(true);
        //执行beforeAll 方法
invokeBeforeAllMethods(context);
}
    }
    ......
return context;
}
......

private void invokeBeforeAllCallbacks(JupiterEngineExecutionContext context) {
    ......
for (BeforeAllCallback callback : registry.getExtensions(BeforeAllCallback.class)) {
      //实现BeforeAllCallback的实现类主要有TimeOUtExtension,SpringExtension, TempDirectory, OutputCaptureExtension
      //这里我们主要关注SpringExtension 相关的处理方法
throwableCollector.execute(() -> callback.beforeAll(extensionContext));
if (throwableCollector.isNotEmpty()) {
break;
}
}
}

  
  //SpringExtension的beforeAll方法
public void beforeAll(ExtensionContext context) throws Exception {
getTestContextManager(context).beforeTestClass();
}

在SpringExtension的beforeAll方法中,初始化了TestContextManager对象,循环遍历TestExecutionListener,执行listener的beforeTestClass方法

这里涉及到了Spring TestContext Framework机制,详细机制讲解可见官方文档

https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testcontext-framework

/**
* Get the {@link TestContextManager} associated with the supplied {@code ExtensionContext}.
* @return the {@code TestContextManager} (never {@code null})
*/
private static TestContextManager getTestContextManager(ExtensionContext context) {
Assert.notNull(context, "ExtensionContext must not be null");
Class<?> testClass = context.getRequiredTestClass();
//获取当前的ExtensionContext中的ExtensionValuesStore对象valuesStore
Store store = getStore(context);

//存在则获取Supplier<Object> storedValue, 里面是个creator function, 通过storedValue.get()执行function创建
return store.getOrComputeIfAbsent(testClass, TestContextManager::new, TestContextManager.class);
}
......


<K, V> Object getOrComputeIfAbsent(Namespace namespace, K key, Function<K, V> defaultCreator) {
//通过namespacce和 testClass 构造compositeKey
CompositeKey compositeKey = new CompositeKey(namespace, key);
Supplier<Object> storedValue = getStoredValue(compositeKey);
if (storedValue == null) {
//构造Supplier 对象,MemoizingSupplier为Supplier的扩展,增加了锁机制
Supplier<Object> newValue = new MemoizingSupplier(() -> defaultCreator.apply(key));
storedValue = Optional.ofNullable(storedValues.putIfAbsent(compositeKey, newValue)).orElse(newValue);
}

//执行defaultCreator.apply(key),即执行public TestContextManager(Class<?> testClass) 方法,创建TestContextManager对象
return storedValue.get();
}


<K, V> V getOrComputeIfAbsent(Namespace namespace, K key, Function<K, V> defaultCreator, Class<V> requiredType) {
Object value = getOrComputeIfAbsent(namespace, key, defaultCreator);
return castToRequiredType(key, value, requiredType);
}


初始化TestContextManager过程中,会初始化TestContextBootstrapper对象,会查找@BootstrapWith的value值并初始化,我们可以看到查找到@SpringBootTest时候会初始化SpringBootTestContextBootstrapper对象

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@ExtendWith(SpringExtension.class)
public @interface SpringBootTest {
......
}


在构造方法中,会构造TestContext对象

//主要构造方法,生成TestContext 对象,注册执行监听器
public TestContextManager(TestContextBootstrapper testContextBootstrapper) {
this.testContext = testContextBootstrapper.buildTestContext();
    //注册发现的监听器,即加入一个TestExecutionListener链表
registerTestExecutionListeners(testContextBootstrapper.getTestExecutionListeners());
}

在buildTestContext()方法中,构造了TestContext 对象,其中会调用父类AbstractTestContextBootstrapper抽象类中的build方法,父类方法主要构造了WebMergedContextConfiguration对象(其中很多查询不同注解生成不同配置的细节,感兴趣同学可以自行阅读),最后通过构造方法生成DefaultTestContext对象

/**
* Construct a new {@code DefaultTestContext} from the supplied arguments.
* @param testClass the test class for this test context
* @param mergedContextConfiguration the merged application context
* configuration for this test context
* @param cacheAwareContextLoaderDelegate the delegate to use for loading
* and closing the application context for this test context
*/
public DefaultTestContext(Class<?> testClass, MergedContextConfiguration mergedContextConfiguration,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
   ......




this.testClass = testClass;
this.mergedContextConfiguration = mergedContextConfiguration;
this.cacheAwareContextLoaderDelegate = cacheAwareContextLoaderDelegate;
}

在getTestExecutionListeners() 方法中获取默认的TestExecutionListener列表,里面通过spi机制会加载配置文件中的listenner, 同时通过监听器后置处理方法 用SpringBootDependencyInjectionTestExecutionListener替换掉DependencyInjectionTestExecutionListener

public Set<Class<? extends TestExecutionListener>> postProcessDefaultTestExecutionListeners(
Set<Class<? extends TestExecutionListener>> listeners) {
Set<Class<? extends TestExecutionListener>> updated = new LinkedHashSet<>(listeners.size());
for (Class<? extends TestExecutionListener> listener : listeners) {
      //springboot中使用新的监听器替换掉spring使用的DependencyInjectionTestExecutionListener
updated.add(listener.equals(DependencyInjectionTestExecutionListener.class)
? SpringBootDependencyInjectionTestExecutionListener.class : listener);
}
return updated;
}

以上流程执行完成node.before方法,接下来执行node.execute方法,当前node为ClassTestDescriptor,execute方法为空直接跳过;执行到子node节点时,通过执行taskContext.getExecutorService().invokeAll(children)方法执行子 node即TestMethodTestDescriptor。

      TestMethodTestDescriptor 中的prepare方法,会调用父context中的TestInstancesProvider初始化TestInstances

public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) {
MutableExtensionRegistry registry = populateNewExtensionRegistry(context);
ThrowableCollector throwableCollector = createThrowableCollector();
MethodExtensionContext extensionContext = new MethodExtensionContext(context.getExtensionContext(),
context.getExecutionListener(), this, context.getConfiguration(), throwableCollector);
throwableCollector.execute(() -> {
//获取ClassTestDescriptor 中注册的Provider并实例
TestInstances testInstances = context.getTestInstancesProvider().getTestInstances(registry);
extensionContext.setTestInstances(testInstances);
});


// @formatter:off
return context.extend()
.withExtensionRegistry(registry)
.withExtensionContext(extensionContext)
.withThrowableCollector(throwableCollector)
.build();
// @formatter:on
}

调用ClassBasedTestDescriptor 中的instantiateTestClass实例化TestInstances

private TestInstances instantiateAndPostProcessTestInstance(JupiterEngineExecutionContext parentExecutionContext,
ExtensionContext extensionContext, ExtensionRegistry registry, ExtensionRegistrar registrar) {
//初始化测试用例实例
TestInstances instances = instantiateTestClass(parentExecutionContext, registry, registrar, extensionContext);
invokeTestInstancePostProcessors(instances.getInnermostInstance(), registry, extensionContext);
// In addition, we register extensions from instance fields here since the
// best time to do that is immediately following test class instantiation
// and post processing.
registerExtensionsFromFields(registrar, this.testClass, instances.getInnermostInstance());
return instances;
}
......


protected TestInstances instantiateTestClass(Optional<TestInstances> outerInstances, ExtensionRegistry registry,
ExtensionContext extensionContext) {


Optional<Object> outerInstance = outerInstances.map(TestInstances::getInnermostInstance);
Object instance = this.testInstanceFactory != null //
? invokeTestInstanceFactory(outerInstance, extensionContext)
//通过构造方法初始化
: invokeTestClassConstructor(outerInstance, registry, extensionContext);
return outerInstances.map(instances -> DefaultTestInstances.of(instances, instance)).orElse(
//返回DefaultTestInstances对象
DefaultTestInstances.of(instance));
}

invokeTestInstancePostProcessors 方法中,会调用TestInstancePostProcessor接口的实现类中的postProcessTestInstance方法,前实现类有MockitoExtension和SpringExtension

private void invokeTestInstancePostProcessors(Object instance, ExtensionRegistry registry,
ExtensionContext context) {


registry.stream(TestInstancePostProcessor.class).forEach(
extension -> executeAndMaskThrowable(() -> extension.postProcessTestInstance(instance, context)));
}

1)在SpringExtension中的实现方法调用prepareTestInstance方法初始化容器,启动springboot。具体执行流程为:

  在prepareTestInstance方法中,遍历testExecutionListeners,依次为ServletTestExecutionListener,其中会初始化加载ApplicationContext,具体在DefaultTestContext对象中查找CacheAwareContextLoaderDelegate对象中的ContextCache,通过前面构建的MergedContextConfiguration key查找对应的ApplicationContext, 不存在通过SmartContextLoader即SpringBootContextLoader加载,最后调用SpringApplication.run方法启动SpringBoot

private void setUpRequestContextIfNecessary(TestContext testContext) {
if (!isActivated(testContext) || alreadyPopulatedRequestContextHolder(testContext)) {
return;
}
//加载ApplicationContext,过程中启动SpringBoot
ApplicationContext context = testContext.getApplicationContext();
......
}

2)在MockitoTestExecutionListener中,查找测试类中的org.mockito开头的注解,如@Mock等,

public void prepareTestInstance(TestContext testContext) throws Exception {
//查找org.mockito库中注解修饰的属性并初始化
//使用mokito库构造注解对象,并注入到对应测试类testInstance中的mock相关属性
initMocks(testContext);

//注入mock的属性,这里注入的是springboot 下的mockbean, spybean等注解修饰的属性
injectFields(testContext);
}


......


private void postProcessFields(TestContext testContext, BiConsumer<MockitoField, MockitoPostProcessor> consumer) {
DefinitionsParser parser = new DefinitionsParser();
//前面构建MergedContextConfiguration 过程中会解析一遍,当前重新解析
parser.parse(testContext.getTestClass());
if (!parser.getDefinitions().isEmpty()) {
//获取MockitoPostProcessor bean
MockitoPostProcessor postProcessor = testContext.getApplicationContext()
.getBean(MockitoPostProcessor.class);
for (Definition definition : parser.getDefinitions()) {
Field field = parser.getField(definition);
if (field != null) {
//注入MockitoField
consumer.accept(new MockitoField(field, testContext.getTestInstance(), definition), postProcessor);
}
}
}
}

3)在 SpringBootDependencyInjectionTestExecutionListener 中,通过injectDependencies方法依赖注入,给testInstance中的其他非mock属性注入对应的值,这里使用的autowireMode AutowireCapableBeanFactory.AUTOWIRE_NO

private TestInstances instantiateAndPostProcessTestInstance(JupiterEngineExecutionContext parentExecutionContext,
ExtensionContext extensionContext, ExtensionRegistry registry, ExtensionRegistrar registrar) {


TestInstances instances = instantiateTestClass(parentExecutionContext, registry, registrar, extensionContext);
invokeTestInstancePostProcessors(instances.getInnermostInstance(), registry, extensionContext);
// In addition, we register extensions from instance fields here since the
// best time to do that is immediately following test class instantiation
// and post processing.
registerExtensionsFromFields(registrar, this.testClass, instances.getInnermostInstance());
return instances;
}

testInstance实例化完成后,返回到TestMethodTestDescriptor中,设置好MethodExtensionContext中testInstances的值。

     下节将继续讲解TestMethodTestDescriptor的execute方法。

    To be continued...

文章转载自测开技术笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论