参考链接

源码分析

概览

image.png

  • JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

从运行分析源码

打印调用链

  1. index: 0 ClassName: java.lang.Thread Method Name : getStackTrace
  2. index: 1 ClassName: com.example.project.FirstTest Method Name : myFirstTest
  3. index: 2 ClassName: sun.reflect.NativeMethodAccessorImpl Method Name : invoke0
  4. index: 3 ClassName: sun.reflect.NativeMethodAccessorImpl Method Name : invoke
  5. index: 4 ClassName: sun.reflect.DelegatingMethodAccessorImpl Method Name : invoke
  6. index: 5 ClassName: java.lang.reflect.Method Method Name : invoke
  7. index: 6 ClassName: org.junit.platform.commons.util.ReflectionUtils Method Name : invokeMethod
  8. index: 7 ClassName: org.junit.jupiter.engine.execution.MethodInvocation Method Name : proceed
  9. index: 8 ClassName: org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation Method Name : proceed
  10. index: 9 ClassName: org.junit.jupiter.engine.extension.TimeoutExtension Method Name : intercept
  11. index: 10 ClassName: org.junit.jupiter.engine.extension.TimeoutExtension Method Name : interceptTestableMethod
  12. index: 11 ClassName: org.junit.jupiter.engine.extension.TimeoutExtension Method Name : interceptTestMethod
  13. index: 12 ClassName: org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall Method Name : lambda$ofVoidMethod$0
  14. index: 13 ClassName: org.junit.jupiter.engine.execution.ExecutableInvoker Method Name : lambda$invoke$0
  15. index: 14 ClassName: org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation Method Name : proceed
  16. index: 15 ClassName: org.junit.jupiter.engine.execution.InvocationInterceptorChain Method Name : proceed
  17. index: 16 ClassName: org.junit.jupiter.engine.execution.InvocationInterceptorChain Method Name : chainAndInvoke
  18. index: 17 ClassName: org.junit.jupiter.engine.execution.InvocationInterceptorChain Method Name : invoke
  19. index: 18 ClassName: org.junit.jupiter.engine.execution.ExecutableInvoker Method Name : invoke
  20. index: 19 ClassName: org.junit.jupiter.engine.execution.ExecutableInvoker Method Name : invoke
  21. index: 20 ClassName: org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor Method Name : lambda$invokeTestMethod$7
  22. index: 21 ClassName: org.junit.platform.engine.support.hierarchical.ThrowableCollector Method Name : execute
  23. index: 22 ClassName: org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor Method Name : invokeTestMethod
  24. index: 23 ClassName: org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor Method Name : execute
  25. index: 24 ClassName: org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor Method Name : execute
  26. index: 25 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : lambda$executeRecursively$6
  27. index: 26 ClassName: org.junit.platform.engine.support.hierarchical.ThrowableCollector Method Name : execute
  28. index: 27 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : lambda$executeRecursively$8
  29. index: 28 ClassName: org.junit.platform.engine.support.hierarchical.Node Method Name : around
  30. index: 29 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : lambda$executeRecursively$9
  31. index: 30 ClassName: org.junit.platform.engine.support.hierarchical.ThrowableCollector Method Name : execute
  32. index: 31 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : executeRecursively
  33. index: 32 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : execute
  34. index: 33 ClassName: java.util.ArrayList Method Name : forEach
  35. index: 34 ClassName: org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService Method Name : invokeAll
  36. index: 35 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : lambda$executeRecursively$6
  37. index: 36 ClassName: org.junit.platform.engine.support.hierarchical.ThrowableCollector Method Name : execute
  38. index: 37 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : lambda$executeRecursively$8
  39. index: 38 ClassName: org.junit.platform.engine.support.hierarchical.Node Method Name : around
  40. index: 39 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : lambda$executeRecursively$9
  41. index: 40 ClassName: org.junit.platform.engine.support.hierarchical.ThrowableCollector Method Name : execute
  42. index: 41 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : executeRecursively
  43. index: 42 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : execute
  44. index: 43 ClassName: java.util.ArrayList Method Name : forEach
  45. index: 44 ClassName: org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService Method Name : invokeAll
  46. index: 45 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : lambda$executeRecursively$6
  47. index: 46 ClassName: org.junit.platform.engine.support.hierarchical.ThrowableCollector Method Name : execute
  48. index: 47 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : lambda$executeRecursively$8
  49. index: 48 ClassName: org.junit.platform.engine.support.hierarchical.Node Method Name : around
  50. index: 49 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : lambda$executeRecursively$9
  51. index: 50 ClassName: org.junit.platform.engine.support.hierarchical.ThrowableCollector Method Name : execute
  52. index: 51 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : executeRecursively
  53. index: 52 ClassName: org.junit.platform.engine.support.hierarchical.NodeTestTask Method Name : execute
  54. index: 53 ClassName: org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService Method Name : submit
  55. index: 54 ClassName: org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor Method Name : execute
  56. index: 55 ClassName: org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine Method Name : execute
  57. index: 56 ClassName: org.junit.platform.launcher.core.EngineExecutionOrchestrator Method Name : execute
  58. index: 57 ClassName: org.junit.platform.launcher.core.EngineExecutionOrchestrator Method Name : execute
  59. index: 58 ClassName: org.junit.platform.launcher.core.EngineExecutionOrchestrator Method Name : lambda$execute$0
  60. index: 59 ClassName: org.junit.platform.launcher.core.EngineExecutionOrchestrator Method Name : withInterceptedStreams
  61. index: 60 ClassName: org.junit.platform.launcher.core.EngineExecutionOrchestrator Method Name : execute
  62. index: 61 ClassName: org.junit.platform.launcher.core.DefaultLauncher Method Name : execute
  63. index: 62 ClassName: org.junit.platform.launcher.core.DefaultLauncher Method Name : execute
  64. index: 63 ClassName: org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher Method Name : execute
  65. index: 64 ClassName: org.junit.platform.launcher.core.SessionPerRequestLauncher Method Name : execute
  66. index: 65 ClassName: org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor Method Name : processAllTestClasses
  67. index: 66 ClassName: org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor Method Name : access$000
  68. index: 67 ClassName: org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor Method Name : stop
  69. index: 68 ClassName: org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor Method Name : stop
  70. index: 69 ClassName: sun.reflect.NativeMethodAccessorImpl Method Name : invoke0
  71. index: 70 ClassName: sun.reflect.NativeMethodAccessorImpl Method Name : invoke
  72. index: 71 ClassName: sun.reflect.DelegatingMethodAccessorImpl Method Name : invoke
  73. index: 72 ClassName: java.lang.reflect.Method Method Name : invoke
  74. index: 73 ClassName: org.gradle.internal.dispatch.ReflectionDispatch Method Name : dispatch
  75. index: 74 ClassName: org.gradle.internal.dispatch.ReflectionDispatch Method Name : dispatch
  76. index: 75 ClassName: org.gradle.internal.dispatch.ContextClassLoaderDispatch Method Name : dispatch
  77. index: 76 ClassName: org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler Method Name : invoke
  78. index: 77 ClassName: com.sun.proxy.$Proxy2 Method Name : stop
  79. index: 78 ClassName: org.gradle.api.internal.tasks.testing.worker.TestWorker$3 Method Name : run
  80. index: 79 ClassName: org.gradle.api.internal.tasks.testing.worker.TestWorker Method Name : executeAndMaintainThreadName
  81. index: 80 ClassName: org.gradle.api.internal.tasks.testing.worker.TestWorker Method Name : execute
  82. index: 81 ClassName: org.gradle.api.internal.tasks.testing.worker.TestWorker Method Name : execute
  83. index: 82 ClassName: org.gradle.process.internal.worker.child.ActionExecutionWorker Method Name : execute
  84. index: 83 ClassName: org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker Method Name : call
  85. index: 84 ClassName: org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker Method Name : call
  86. index: 85 ClassName: worker.org.gradle.process.internal.worker.GradleWorkerMain Method Name : run
  87. index: 86 ClassName: worker.org.gradle.process.internal.worker.GradleWorkerMain Method Name : main


SessionPerRequestLauncher

public void execute(LauncherDiscoveryRequest launcherDiscoveryRequest, TestExecutionListener... listeners) {
        try (LauncherSession session = createSession()) {
            session.getLauncher().execute(launcherDiscoveryRequest, listeners);
        }
    }
  • LauncherDiscoveryRequest 接口
  • DefaultDiscoveryRequest

  • TestExecutionListener 空的

DefaultLauncherSession

  • getLauncher

    DefaultLauncher

    ```java public void execute(LauncherDiscoveryRequest discoveryRequest, TestExecutionListener… listeners) {
      Preconditions.notNull(discoveryRequest, "LauncherDiscoveryRequest must not be null");
      Preconditions.notNull(listeners, "TestExecutionListener array must not be null");
      Preconditions.containsNoNullElements(listeners, "individual listeners must not be null");
      execute(InternalTestPlan.from(discover(discoveryRequest, EXECUTION)), listeners);
    
    }

private void execute(InternalTestPlan internalTestPlan, TestExecutionListener[] listeners) { executionOrchestrator.execute(internalTestPlan, listeners); }


<a name="TBbR8"></a>
#### EngineExecutionOrchestrator
```java
void execute(InternalTestPlan internalTestPlan, TestExecutionListener... listeners) {
        internalTestPlan.markStarted();
        LauncherDiscoveryResult discoveryResult = internalTestPlan.getDiscoveryResult();
        ConfigurationParameters configurationParameters = discoveryResult.getConfigurationParameters();
        ListenerRegistry<TestExecutionListener> listenerRegistry = buildListenerRegistryForExecution(listeners);
        withInterceptedStreams(configurationParameters, listenerRegistry, testExecutionListener -> {
            testExecutionListener.testPlanExecutionStarted(internalTestPlan);
            execute(discoveryResult, new ExecutionListenerAdapter(internalTestPlan, testExecutionListener));
            testExecutionListener.testPlanExecutionFinished(internalTestPlan);
        });
    }

....
public void execute(LauncherDiscoveryResult discoveryResult, EngineExecutionListener listener) {
        for (TestEngine testEngine : discoveryResult.getTestEngines()) {
            TestDescriptor engineDescriptor = discoveryResult.getEngineTestDescriptor(testEngine);
            if (engineDescriptor instanceof EngineDiscoveryErrorDescriptor) {
                listener.executionStarted(engineDescriptor);
                listener.executionFinished(engineDescriptor,
                    TestExecutionResult.failed(((EngineDiscoveryErrorDescriptor) engineDescriptor).getCause()));
            }
            else {
                execute(engineDescriptor, listener, discoveryResult.getConfigurationParameters(), testEngine);
            }
        }
    }
.....
private void execute(TestDescriptor engineDescriptor, EngineExecutionListener listener,
            ConfigurationParameters configurationParameters, TestEngine testEngine) {

        OutcomeDelayingEngineExecutionListener delayingListener = new OutcomeDelayingEngineExecutionListener(listener,
            engineDescriptor);
        try {
            testEngine.execute(new ExecutionRequest(engineDescriptor, delayingListener, configurationParameters));
            delayingListener.reportEngineOutcome();
        }
        catch (Throwable throwable) {
            UnrecoverableExceptions.rethrowIfUnrecoverable(throwable);
            delayingListener.reportEngineFailure(new JUnitException(
                String.format("TestEngine with ID '%s' failed to execute tests", testEngine.getId()), throwable));
        }
    }

HierarchicalTestEngine

image.png

HierarchicalTestExecutor

  • 执行请求 ExecutionReuqest
  • 上下文 rootContext
  • 执行服务, executorService
  • 异常收集器的工厂

image.png

HierarchicalTestExecutorService //是一个接口

  • 这里看他的实现类 SameThreadHierarchicalTestExecutorService

image.png

TestTask 相关有TestTaskContext 与 TestDescriptor 是个接口

NodeTeskTask

  • execute主流程
  • executeRecursively

    private void executeRecursively() {
          taskContext.getListener().executionStarted(testDescriptor);
          started = true;
    
          throwableCollector.execute(() -> {
              node.around(context, ctx -> {
                  context = ctx;
                  throwableCollector.execute(() -> {
                      // @formatter:off
                      List<NodeTestTask<C>> children = testDescriptor.getChildren().stream()
                              .map(descriptor -> new NodeTestTask<C>(taskContext, descriptor))
                              .collect(toCollection(ArrayList::new));
                      // @formatter:on
    
                      context = node.before(context);
    
                      final DynamicTestExecutor dynamicTestExecutor = new DefaultDynamicTestExecutor();
                      context = node.execute(context, dynamicTestExecutor);
    
                      if (!children.isEmpty()) {
                          children.forEach(child -> child.setParentContext(context));
                          taskContext.getExecutorService().invokeAll(children);
                      }
    
                      throwableCollector.execute(dynamicTestExecutor::awaitFinished);
                  });
    
                  throwableCollector.execute(() -> node.after(context));
              });
          });
      }
    
  • Node 接口

image.png
image.png

  • TestMethodTestDescriptor 实现了Node接口
    • invokeTestMethod
  • ExecutableInvoker

  • InvocationInterceptorChain
  • MethodInvocation

自上而下的梳理

Node , TestDescriptor, TestEngine, TestTask , ExecutionRequest 与重要的实现类

Node 接口

  • 是有关本节点的运行控制逻辑,如是否skip,运行 before和after 的代码等

    TestDescriptor 接口

  • 有Test 和Container两种类型 或两中都是,有很多树状结构数据维护的方法,

    TestEngine 接口

  • 主要是下面两个重要方法

  • TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId);
  • void execute(ExecutionRequest request);

    HierarchicalTestEngine

  • 实现TestEngine 接口

  • 主要就是实现execute 方法,
  • 根据传入的ExecutionRequest 类型参数 创建了HierarchicalTestExecutorService的实现类SameThreadHierarchicalTestExecutorService 实例来执行
  • new 了一个 HierarchicalTestExecutor 实例,把所有相关参数包括 HierarchicalTestExecutorService实例都传了进去
  • HierarchicalTestExecutor 实例里面又调了HierarchicalTestExecutorService的 submit 来执行,入参是一个TeskTask实例,

HierarchicalTestExecutorService 是一个接口 主要三个方法

  • Future submit(TestTask testTask);
  • void invokeAll(List<? extends TestTask> testTasks);
  • void close();
  • 实现类

ForkJoinPoolHierarchicalTestExecutorService, SameThreadHierarchicalTestExecutorService

SameThreadHierarchicalTestExecutorService

  • 实现 submit 和 invokeAll方法主要是调用 TestTask的execute 实现

TestTask

  • HierarchicalTestExecutorService的内部接口
  • 定义了getExecutionMode getResourceLock execute 方法

NodeTestTask 实现了 TestTask

  • 实现了execute方法
  • 主要逻辑在 executeRecursively 这个方法内

    ExecutionRequest

  • 三个属性

      private final TestDescriptor rootTestDescriptor;
    
      private final EngineExecutionListener engineExecutionListener;
    
      private final ConfigurationParameters configurationParameters;
    

    AbstractTestDescriptor 实现 TestDescriptor

    • TestSource 类型的属性,TestSource是个接口
    • TestDescriptor 类型的 parent 和children属性, 是一个树的结构
    • 其它是displayName 与 uniqueId , UniqueID 这个类型的意义? //TODO

      JupiterTestDescriptor 继承AbstractTestDescriptor也实现了Node接口

    • 公有方法,

      • getExecutionMode
      • toExecutionMode
      • shouldBeSkipped
      • prepare //没实现
      • cleanUp

        ClassBasedTestDescriptor 继承自JupiterTestDescriptor

    • 属性 ```java private final Class<?> testClass; protected final Set tags; protected final Lifecycle lifecycle;

      private ExecutionMode defaultChildExecutionMode; private TestInstanceFactory testInstanceFactory; private List beforeAllMethods; private List afterAllMethods;


   - 主要是三个public方法实现,
      - prepare
         - 注册extensions  // extension 这个后面再仔细分析下
         - 主要是构建一个 JupiterEngineExecutionContext 实例
```java
    return context.extend()
                .withTestInstancesProvider(testInstancesProvider(context, extensionContext))
                .withExtensionRegistry(registry)
                .withExtensionContext(extensionContext)
                .withThrowableCollector(throwableCollector)
                .build();
  - before
  - after

JupiterEngineExecutionContext

TestEngine的实现类

HierarchicalTestEngine, JupiterTestEngine, SuiteTestEngine, VintageTestEngine
[

](https://junit.org/junit5/docs/current/api/org.junit.jupiter.engine/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptor.html)