我们先看看TestMethodTestDescriptor的execute方法
public JupiterEngineExecutionContext execute(JupiterEngineExecutionContext context,DynamicTestExecutor dynamicTestExecutor) throws Exception {ThrowableCollector throwableCollector = context.getThrowableCollector();// @formatter:off//执行BeforeEachCallback接口实现类,包括MockitoExtension和SpringExtensioninvokeBeforeEachCallbacks(context);if (throwableCollector.isEmpty()) {//执行用例中的beforeEach方法invokeBeforeEachMethods(context);if (throwableCollector.isEmpty()) {//执行beforeTestExecution方法invokeBeforeTestExecutionCallbacks(context);if (throwableCollector.isEmpty()) {invokeTestMethod(context, dynamicTestExecutor);}invokeAfterTestExecutionCallbacks(context);}invokeAfterEachMethods(context);}invokeAfterEachCallbacks(context);// @formatter:onthrowableCollector.assertEmpty();return context;}
我们详细分析下具体执行步骤:
(一)
invokeBeforeEachCallbacks 方法执行 registry中实现了接口BeforeEachCallback的扩展类,我们主要看SpringExtension的实现;对应实现的方法为SpringExtension 中的 beforeEach方法
public void beforeEach(ExtensionContext context) throws Exception {//获取之前生成的testInstance,已经实例化的测试类Object testInstance = context.getRequiredTestInstance();//当前需要执行的测试方法Method testMethod = context.getRequiredTestMethod();//执行TestContextManager的beforeTestMethod方法getTestContextManager(context).beforeTestMethod(testInstance, testMethod);}
在执行TestContextManager的beforeTestMethod方法时,和之前执行prepareTestInstance方法类似,实际我们可以发现,TestContextManager中执行pre/post 等hook方法时,都是遍历TestExecutionListener列表,依次调用每个Listener的对应方法
public void beforeTestMethod(Object testInstance, Method testMethod) throws Exception {String callbackName = "beforeTestMethod";prepareForBeforeCallback(callbackName, testInstance, testMethod);for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {try {testExecutionListener.beforeTestMethod(getTestContext());}catch (Throwable ex) {handleBeforeException(ex, callbackName, testExecutionListener, testInstance, testMethod);}}}
我们看下主要的Listener对应的beforeTestMethod方法
MockitoTestExecutionListener
因为在prepareTestInstance方法中,已经初始化了mock相关bean和注入对应测试实例,所以在这里有个判断,是否有REINJECT_DEPENDENCIES_ATTRIBUTE属性,如果有重新初始化和重新注入,当前不走这个处理流程
public void beforeTestMethod(TestContext testContext) throws Exception {//当前无此属性,不走重新初始化和注入流程if (Boolean.TRUE.equals(testContext.getAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE))) {initMocks(testContext);reinjectFields(testContext);}}
SpringBootDependencyInjectionTestExecutionListener
因为在prepareTestInstance方法中,已经自动注入被Spring 相关注解@Resource 等修饰的bean,同样判断是否有有
REINJECT_DEPENDENCIES_ATTRIBUTE属性,当前不走这个处理流程
public void beforeTestMethod(TestContext testContext) throws Exception {//当前无此属性,不重新注入if (Boolean.TRUE.equals(testContext.getAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE))) {if (logger.isDebugEnabled()) {logger.debug("Reinjecting dependencies for test context [" + testContext + "].");}injectDependencies(testContext);}}
ResetMocksTestExecutionListener
重置被 MockReset标记的bean
public void beforeTestMethod(TestContext testContext) throws Exception {if (MOCKITO_IS_PRESENT) {//重置处理流程resetMocks(testContext.getApplicationContext(), MockReset.BEFORE);}}
当前我们使用mock的bean为Server bean,MockReset属性为MockReset.AFTER,所以会在afterTestMethod方法中reset
public enum MockReset {/*** Reset the mock before the test method runs.*/BEFORE,/*** Reset the mock after the test method runs.*/AFTER,/*** Don't reset the mock.*/NONE;......}.....private void resetMocks(ConfigurableApplicationContext applicationContext, MockReset reset) {ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();//取得容器中beanDefinition名称String[] names = beanFactory.getBeanDefinitionNames();Set<String> instantiatedSingletons = new HashSet<>(Arrays.asList(beanFactory.getSingletonNames()));for (String name : names) {BeanDefinition definition = beanFactory.getBeanDefinition(name);if (definition.isSingleton() && instantiatedSingletons.contains(name)) {Object bean = beanFactory.getSingleton(name);//获取bean的MockReset属性if (reset.equals(MockReset.get(bean))) {//重置,mockbean失效Mockito.reset(bean);}}}try {MockitoBeans mockedBeans = beanFactory.getBean(MockitoBeans.class);for (Object mockedBean : mockedBeans) {if (reset.equals(MockReset.get(mockedBean))) {Mockito.reset(mockedBean);}}}catch (NoSuchBeanDefinitionException ex) {// Continue}if (applicationContext.getParent() != null) {//如果存在父容器,循环处理父容器中的mock beanresetMocks(applicationContext.getParent(), reset);}}
(二)
接下来执行invokeBeforeEachMethods方法,即执行我们的beforeEach方法init();
private void invokeBeforeEachMethods(JupiterEngineExecutionContext context) {ExtensionRegistry registry = context.getExtensionRegistry();invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(BeforeEachMethodAdapter.class, context,(adapter, extensionContext) -> {try {//执行adapter的invokeBeforeEachMethod方法adapter.invokeBeforeEachMethod(extensionContext, registry);}catch (Throwable throwable) {invokeBeforeEachExecutionExceptionHandlers(extensionContext, registry, throwable);}});}......private <T extends Extension> void invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(Class<T> type,JupiterEngineExecutionContext context, CallbackInvoker<T> callbackInvoker) {ExtensionRegistry registry = context.getExtensionRegistry();ExtensionContext extensionContext = context.getExtensionContext();ThrowableCollector throwableCollector = context.getThrowableCollector();//获取type类型的extension,即BeforeEachMethodAdapter接口类型for (T callback : registry.getExtensions(type)) {//执行lambda 表达式callbackInvoker,回调throwableCollector.execute(() -> callbackInvoker.invoke(callback, extensionContext));if (throwableCollector.isNotEmpty()) {break;}}}
执行before or after method方法会调用ClassBasedTestDescriptor中对应注册的adapter方法
private BeforeEachMethodAdapter synthesizeBeforeEachMethodAdapter(Method method) {return (extensionContext, registry) -> invokeMethodInExtensionContext(method, extensionContext, registry,InvocationInterceptor::interceptBeforeEachMethod);}private AfterEachMethodAdapter synthesizeAfterEachMethodAdapter(Method method) {return (extensionContext, registry) -> invokeMethodInExtensionContext(method, extensionContext, registry,InvocationInterceptor::interceptAfterEachMethod);}private void invokeMethodInExtensionContext(Method method, ExtensionContext context, ExtensionRegistry registry,VoidMethodInterceptorCall interceptorCall) {TestInstances testInstances = context.getRequiredTestInstances();Object target = testInstances.findInstance(method.getDeclaringClass()).orElseThrow(() -> new JUnitException("Failed to find instance for method: " + method.toGenericString()));//调用ExecutableInvoker 的invoke方法executableInvoker.invoke(method, target, context, registry,ReflectiveInterceptorCall.ofVoidMethod(interceptorCall));}
ExecutableInvoker类中涉及调用拦截链,会查找InvocationInterceptor扩展依次执行;当前存在一个默认实现是TimoeoutExtension,其中interceptBeforeEachMethod方法中获取执行timeOut相关配置,如果方法上面有注解@Timeout,读取对应的值,否则获取默认配置,包括
"junit.jupiter.execution.timeout.beforeeach.method.default"
或者"junit.jupiter.execution.timeout.default"
配置对应的值,如果都没有,则超时时间为null;我们当前举的例子中,没有对应配置,所以为null
private <E extends Executable, T> T invoke(Invocation<T> originalInvocation,ReflectiveInvocationContext<E> invocationContext, ExtensionContext extensionContext,ExtensionRegistry extensionRegistry, ReflectiveInterceptorCall<E, T> call) {//调用interceptorChain的invokereturn interceptorChain.invoke(originalInvocation, extensionRegistry, (interceptor,wrappedInvocation) -> call.apply(interceptor, wrappedInvocation, invocationContext, extensionContext));}// class InvocationInterceptorChain 中的拦截链方法public <T> T invoke(Invocation<T> invocation, ExtensionRegistry extensionRegistry, InterceptorCall<T> call) {List<InvocationInterceptor> interceptors = extensionRegistry.getExtensions(InvocationInterceptor.class);if (interceptors.isEmpty()) {return proceed(invocation);}return chainAndInvoke(invocation, call, interceptors);}
最终调用MethodInvocation类中的proceed方法执行before方法
public T proceed() {//调用ReflectionUtils中的invokeMethod 执行方法return (T) ReflectionUtils.invokeMethod(method, target.orElse(null), arguments);}......//执行demo中的beforeEach方法@BeforeEachpublic void init(){System.out.println("Init base test");}
(三)
接着执行invokeBeforeTestExecutionCallbacks方法,过滤帅选的extension type是 BeforeTestExecutionCallback.class,该接口的实现类还是我们熟悉的SpringExtension
private void invokeBeforeTestExecutionCallbacks(JupiterEngineExecutionContext context) {invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(BeforeTestExecutionCallback.class, context,(callback, extensionContext) -> callback.beforeTestExecution(extensionContext));}
我们看到SpringExtension中还是调用类TestContextManager的beforeTestExecution方法
getTestContextManager(context).beforeTestExecution(testInstance, testMethod);
在TestContextManager的beforeTestExecution方法中,和之前讲述的执行其他方法一样,依次遍历TestExecutionListener并执行对应方法
testExecutionListener.beforeTestExecution(getTestContext());
当前系统默认实现只有EventPublishingTestExecutionListener,用于发布BeforeTestExecutionEvent事件
(四)
接下来到了执行我们的测试方法,执行invokeTestMethod
protected void invokeTestMethod(JupiterEngineExecutionContext context, DynamicTestExecutor dynamicTestExecutor) {ExtensionContext extensionContext = context.getExtensionContext();ThrowableCollector throwableCollector = context.getThrowableCollector();throwableCollector.execute(() -> {try {Method testMethod = getTestMethod();Object instance = extensionContext.getRequiredTestInstance();//调用ExecutableInvoker 的invoke方法, 和执行beforeEach方法类似,这里就不在讲解executableInvoker.invoke(testMethod, instance, extensionContext, context.getExtensionRegistry(),interceptorCall);}catch (Throwable throwable) {//捕捉测试方法中的异常,比如断言BlacklistedExceptions.rethrowIfBlacklisted(throwable);//TestExecutionExceptionHandler扩展机制,可以实现自己的异常handle//可以参考官方文档 https://junit.org/junit5/docs/current/user-guide/#extensions-exception-handlinginvokeTestExecutionExceptionHandlers(context.getExtensionRegistry(), extensionContext, throwable);}});}
其余的执行流程包括invokeAfterTestExecutionCallbacks,invokeAfterEachMethods和invokeAfterEachCallbacks,和对应before方法执行流程基本一致; 注意在invokeAfterEachCallbacks中,调用TestContextManager的afterTestMethod依次遍历
TestExecutionListener做一些后置处理工作,包括reset mockbean, 清理testContext属性等
(五)
剩下的流程为清理相关资源,由于在executeRecursively方法中,依次调用子节点循环执行,清理资源相关方法会依次相反执行,由method->class->engine层次执行级别依次清理相关资源
1) 调用cleanUp方法关闭相应node context对象
private void cleanUp() {throwableCollector.execute(() -> node.cleanUp(context));}//close context资源public void cleanUp(JupiterEngineExecutionContext context) throws Exception {context.close();}
2)通知EngineExecutionListener执行结果
taskContext.getListener().executionFinished(testDescriptor, throwableCollector.toTestExecutionResult())
3)当node节点为调用SpringExtension通知TestExecutionListener执行afterTestClass方法
public void afterAll(ExtensionContext context) throws Exception {try {getTestContextManager(context).afterTestClass();}finally {getStore(context).remove(context.getRequiredTestClass());}}
至此,我们使用Junit5 从加载测试用例到执行测试用例流程基本流程分享完成;当然,其中很多细节,比如异常处理流程,执行链流程,Junit5强大的扩展机制等等,需要持续的深入学习,望大家读后有所收获。




