idea中junit运行的入口
com.intellij.junit4.JUnit4IdeaTestRunner
单个类运行时,会首先调用
org.junit.runners.ParentRunner#getChildren()
该方法生成每个@Test注解标注的TestCase
junit 关键的抽象
RunnerBuilder 为被测试的类构建runner,默认的runner有2种,默认JUnit4Builder,AnnotatedBuilder即我们在测试用例中@Runwith注解标注的runner
RunnerBuilder {
/**
* Override to calculate the correct runner for a test class at runtime.
*
* @param testClass class to be run
* @return a Runner
* @throws Throwable if a runner cannot be constructed
*/
public abstract Runner runnerForClass(Class<?> testClass) throws Throwable;
}
Runner负责运行我们的测试类,常见的Runner有
BlockJUnit4ClassRunner-junit默认的测试运行runner
SpringJUnit4ClassRunner,-带spring容器启动的runner
MockitoJUnitRunner-与mockito集成的runner
/**
* 自定义的runner必须提供一个带class类型参数的构造函数,
*/
public abstract class Runner implements Describable {
/**
* Run the tests for this runner.
*
* @param notifier will be notified of events while tests are being run--tests being
* started, finishing, and failing
*/
public abstract void run(RunNotifier notifier);
}
public abstract class ParentRunner<T> extends Runner implements Filterable,
Sortable {
}
// 执行每个@Test注解的方法
// 该方法在SpringJUnit4ClassRunner中被重写,增加了前置和后置的处理类
// junit为每个@Test方法执行封装成Statement语句(装饰模式)
protected abstract void runChild(T child, RunNotifier notifier);
junit 运行testCase的流程
- 为每个@TestCase注解的方法生成TestClass对象
- 调用@Before注解方法
- 调用@Test注解方法
- 调用@After注解方法
spring与junit结合点
SpringJUnit4ClassRunner在创建时,会持有testContextManager,testContextManager会持有一组 List testExecutionListeners。
SpringJUnit4ClassRunner通过在每个@Test注解标志的测试用例执行语句增加前置处理的statement, 增加钩子方法,实现对spring容器的初始化,同时springJUnit4ClassRunner还重载了createTest()方法,增加prepareTestInstance()钩子函数调用实现spring容器初始化
protected Statement methodBlock(FrameworkMethod frameworkMethod) {
Object testInstance;
try {
testInstance = new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest();
}
}.run();
}
catch (Throwable ex) {
return new Fail(ex);
}
Statement statement = methodInvoker(frameworkMethod, testInstance);
statement = withBeforeTestExecutionCallbacks(frameworkMethod, testInstance, statement);
statement = withAfterTestExecutionCallbacks(frameworkMethod, testInstance, statement);
statement = possiblyExpectingExceptions(frameworkMethod, testInstance, statement);
statement = withBefores(frameworkMethod, testInstance, statement);
statement = withAfters(frameworkMethod, testInstance, statement);
statement = withRulesReflectively(frameworkMethod, testInstance, statement);
statement = withPotentialRepeat(frameworkMethod, testInstance, statement);
statement = withPotentialTimeout(frameworkMethod, testInstance, statement);
return statement;
}
-
RunBeforeTestClassCallbacks - 在类级别的执行
-
RunBeforeTestExecutionCallbacks - beforeTestExecution 在test执行前执行
-
RunBeforeTestMethodCallbacks - beforeTestMethod 在方法执行之前执行
-
prepareTestInstance - 创建测试用例的类实例时执行
-
创建类实例时,重载如下方法
org.junit.runners.BlockJUnit4ClassRunner#createTest org.springframework.test.context.junit4.SpringJUnit4ClassRunner#createTest此时Spring会循环调用TestExecutionListener.prepareTestInstance(TestContext context)
org.springframework.test.context.TestExecutionListener#prepareTestInstanceTestExecutionListener由 TestContextManager管理和初始化
spring默认的TestExecutionListener
ServletTestExecutionListener DirtiesContextBeforeModesTestExecutionListener // 执行核心的依赖准入 DependencyInjectionTestExecutionListener DirtiesContextTestExecutionListener TransactionalTestExecutionListener SqlScriptsTestExecutionListenerspring boot 新增如下TestExecutionListener
// mockiot注入 MockitoTestExecutionListener ResetMocksTestExecutionListener
TestExecutionListener加载方式
- 放置在META-INF目录下spring.factories方式加载 参考spring-test配置
org.springframework.test.context.TestExecutionListener = \
org.springframework.test.context.web.ServletTestExecutionListener,\
org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener,\
org.springframework.test.context.support.DependencyInjectionTestExecutionListener,\
org.springframework.test.context.support.DirtiesContextTestExecutionListener,\
org.springframework.test.context.transaction.TransactionalTestExecutionListener,\
org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener
- 在测试类中配置,这样会覆盖默认加载方式,即上面提到的方式
junit中spring容器启动方式
junit在为每个@Test方法执行前创建被测对象的类实例,此时Spring容器会通过TestExecutionListener.prepareTestInstance() 完成spring容器的创建
同时spring为性能考虑针对同一个类的测试用例会缓存spring容器,仅创建一次
spring-test启动上下文自定义customizeContext
spring-test通过MergedContextConfiguration中的自定义指定的ContextCustomizer实现spring容器的自定义 ContextCustomizer通过META-INFO/spring.factoring 加载
spring 默认定义的自定义
org.springframework.test.context.ContextCustomizerFactory = \
org.springframework.test.context.web.socket.MockServerContainerContextCustomizerFactory
spring boot 扩充定义
org.springframework.test.context.ContextCustomizerFactory=\
org.springframework.boot.test.context.ImportsContextCustomizerFactory,\
org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizerFactory,\
org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory,\
org.springframework.boot.test.mock.mockito.MockitoContextCustomizerFactory,\
org.springframework.boot.test.web.client.TestRestTemplateContextCustomizerFactory,\
org.springframework.boot.test.web.reactive.server.WebTestClientContextCustomizerFactory
其中MockitoContextCustomizerFactory 实现MockitoPostProcessor后者处理器向spring容器注入,该后置处理器向spring容器注入所有配置的mock bean, 通过此后者处理器创建的spring bean均由该类代理创建实例(ObjectFactory)