JUnit4 解析(施工中)
JUnit4 解析(施工中)
如何解读源码
先了解怎样使用,有什么功能
JUnit4文档 https://junit.org/junit4/faq.html#overview_1
从功能出发,研究工作原理
阅读源码
如何阅读源码?
推荐:Idea插件 Sequence diagram
参考资料:
https://www.jianshu.com/p/77c491e7aef8
https://blog.saymagic.cn/2016/09/30/understand-Junit.html
- 如何去阅读并学习一些优秀的开源框架的源码?
- 关于阅读开源项目的源码,有哪些经验值得分享?
- 如何阅读程序源代码?
- 程序员阅读源码是一种什么心态?源码对编程意义何在?如何才能更好阅读代码?
- 如何高效阅读他人的代码?
- 为什么我们要阅读源码?
- 如何阅读一份代码?
- 为什么要学习源码以及如何快速阅读理解源码
- 分析 JUnit 框架源代码 https://developer.ibm.com/zh/articles/j-lo-junit-src/
JUnitCore
runMain:
JUnitCore:运行测试的门面,支持JUnit 4、JUnit 3.8.x和混合的测试
JUnitSystem:退出与PrintStream
JUnitCommandLineParseResult:parse命令行信息、类
Result:运行测试的结果,负责收集和总结测试的信息,统计运行的测试数,失败数,运行时间、开始时间
RunListener:监听器,注册Notifier,可接收到运行时的事件。该类全为抽象方法,需要override方法才能接收事件
RunNotifier:如果写了自定义的Runner,需要提醒JUnit运行测试的进度
Computer:计算runners和suites的策略
Request:将被执行的测试的抽象,支持过滤与排序。Request 会为每个类创建一个Runner
ParentRunner: Runner树的父节点,提供父节点的功能 org.junit.runners.ParentRunner
Runner: 运行测试并通知RunNotifier。Runner默认的实现保证测试用例的实例在测试运行前生成。为了垃圾收集,Runner不会保留测试实例的引用。
默认实现:org.junit.runners.BlockJUnit4ClassRunner
Filter: 过滤将被运行的测试
Description: 描述将被运行的测试以及已运行的测试。描述可以是原子的(只有一个测试),也可以是一组测试(包含子测试)。使用方式:提供测试运行前后的列表。(IDE的treeview)
Expect exception 的实现:
public class ExpectException extends Statement {
private final Statement next;
private final Class<? extends Throwable> expected;
public ExpectException(Statement next, Class<? extends Throwable> expected) {
this.next = next;
this.expected = expected;
}
@Override
public void evaluate() throws Exception {
boolean complete = false;
try {
next.evaluate();
complete = true;
} catch (AssumptionViolatedException e) {
if (!expected.isAssignableFrom(e.getClass())) {
throw e;
}
} catch (Throwable e) {
if (!expected.isAssignableFrom(e.getClass())) {
String message = "Unexpected exception, expected<"
+ expected.getName() + "> but was<"
+ e.getClass().getName() + ">";
throw new Exception(message, e);
}
}
if (complete) {
throw new AssertionError("Expected exception: "
+ expected.getName());
}
}
}测试是怎么跑起来的?
写一个main,调用一个简单测试
JUnitCore
public class App {
public static void main(String[] args) {
org.junit.runner.JUnitCore.main("myExp.SimpleTest");
}
}public class SimpleTest {
@Test
public void testAssertEquals() {
assertEquals("failure - strings are not equal", "text", "text");
} public static void main(String... args) {
Result result = new JUnitCore().runMain(new RealSystem(), args);
System.exit(result.wasSuccessful() ? 0 : 1);
} Result runMain(JUnitSystem system, String... args) {
// 打印版本号
system.out().println("JUnit version " + Version.id());
// 读取命令行参数
// private final List<String> filterSpecs = new ArrayList<String>();
// private final List<Class<?>> classes = new ArrayList<Class<?>>();
// private final List<Throwable> parserErrors = new ArrayList<Throwable>();
JUnitCommandLineParseResult jUnitCommandLineParseResult = JUnitCommandLineParseResult.parse(args);
// 监听器,用于打印信息
RunListener listener = new TextListener(system);
addListener(listener);
// 创建Request
return run(jUnitCommandLineParseResult.createRequest(defaultComputer()));
}defaultComputer方法,创建一个Computer
Represents a strategy for computing runners and suites.
static Computer defaultComputer() {
return new Computer();
}JUnitCommandLineParseResult
读取命令行参数,存储Filter列表与类列表
类获取:使用当前线程的类加载器,如果当前线程没有类加载器,则使用org.junit.internal.Classes 的类加载器
org.junit.runner.JUnitCommandLineParseResult#createRequest
createRequest 方法:
创建Request
应用Filter (--filter),只运行指定的测试。
Command-line options
Pull request #647: Support command-line
--filterparam.When running JUnit from the command line, a command-line parameter can be supplied using
--filter, which supplies a filter that will restrict which tests and subtests from the rest of the command will be run. For example, this will run only the tests in ExampleTestSuite that are in categories Cat1 or Cat2:java org.junit.runner.JUnitCore \ --filter=org.junit.experimental.categories.IncludeCategories=pkg.of.Cat1,pkg.of.Cat2 \ com.example.ExampleTestSuiteIn general, the argument to
--filtershould beClassName=param, whereClassNamenames an implementation ofFilterFactory, whosecreateFiltermethod will be called with an instance ofFilterFactoryParamsthat contains"param", in order to return the filter to be applied.
public Request createRequest(Computer computer) {
if (parserErrors.isEmpty()) {
// 创建 Request
Request request = Request.classes(
computer, classes.toArray(new Class<?>[classes.size()]));
// 应用Filter(装饰器模式)
return applyFilterSpecs(request);
} else {
return errorReport(new InitializationError(parserErrors));
}
}Request
org.junit.runner.Request#classes(org.junit.runner.Computer, java.lang.Class<?>...)
创建一个Reuqest,默认使用 AllDefaultPossibilitiesBuilder 实现
public static Request classes(Computer computer, Class<?>... classes) {
try {
AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder();
// 调用Computer的方法获取Suite
Runner suite = computer.getSuite(builder, classes);
return runner(suite);
} catch (InitializationError e) {
return runner(new ErrorReportingRunner(e, classes));
}
}
org.junit.runner.Request#runner
/**
* @param runner the runner to return
* @return a <code>Request</code> that will run the given runner when invoked
*/
public static Request runner(final Runner runner) {
// 返回 Request,并覆盖getRunner的方法,把获取的Runner返回
return new Request() {
@Override
public Runner getRunner() {
return runner;
}
};
}Computer
Conputer 是运行 runners 和 suites 的策略
public class Computer {
/**
* Returns a new default computer, which runs tests in serial order
*/
public static Computer serial() {
return new Computer();
}
/**
* Create a suite for {@code classes}, building Runners with {@code builder}.
* Throws an InitializationError if Runner construction fails
*/
public Runner getSuite(final RunnerBuilder builder,
Class<?>[] classes) throws InitializationError {
return new Suite(new RunnerBuilder() {
/**
* 获取类对应的 Runner
* 默认实现 org.junit.internal.builders.AllDefaultPossibilitiesBuilder#runnerForClass(java.lang.Class)
* @param testClass class to be run
*/
@Override
public Runner runnerForClass(Class<?> testClass) throws Throwable {
// 实际调用了传入RunnerBuilder的 runnerForClass方法
return getRunner(builder, testClass);
}
}, classes) {
@Override
protected String getName() {
/*
* #1320 The generated suite is not based on a real class so
* only a 'null' description can be generated from it. This name
* will be overridden here.
*/
return "classes";
}
};
}
/**
* Create a single-class runner for {@code testClass}, using {@code builder}
*/
protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable {
return builder.runnerForClass(testClass);
// org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass
}
}AllDefaultPossibilitiesBuilder
public class AllDefaultPossibilitiesBuilder extends RunnerBuilder {
private final boolean canUseSuiteMethod;
/**
* @since 4.13
*/
public AllDefaultPossibilitiesBuilder() {
canUseSuiteMethod = true;
}
/**
* @deprecated used {@link #AllDefaultPossibilitiesBuilder()}.
*/
@Deprecated
public AllDefaultPossibilitiesBuilder(boolean canUseSuiteMethod) {
this.canUseSuiteMethod = canUseSuiteMethod;
}
@Override
public Runner runnerForClass(Class<?> testClass) throws Throwable {
List<RunnerBuilder> builders = Arrays.asList(
ignoredBuilder(), // 忽略Ignore注解的Runner
annotatedBuilder(),// @RunWith 实现
/*
* canUseSuiteMethod 默认是 trues
* 查询testClass 是否有suite method
* testClass.getMethod("suite");
*/
suiteMethodBuilder(),
/*
* 使用class.isAssignableFrom() 判断是否 JUnit 4 测试 isPre4Test进行判断
* A.isAssignableFrom(B) A 是 B的同类或是 B的superclass 或 superinterface
*/
junit3Builder(),
junit4Builder()); // 默认实现: Runner BlockJUnit4ClassRunner
for (RunnerBuilder each : builders) {
// safeRunnerForClass 方法有两个功能,接下来详解
// 为每个类都返回一个Runner,不管有没有错误
Runner runner = each.safeRunnerForClass(testClass);
if (runner != null) {
return runner;
}
}
return null;
}RunnerBuilder
org.junit.runners.model.RunnerBuilder#safeRunnerForClass
如果找到了Runner则配置Runner,否则返回null
configureRunner:
- 应用OrderWith注解的顺序
- 捕获并存储异常,然后返回一个报告错误的Runner:ErrorReportingRunner
public Runner safeRunnerForClass(Class<?> testClass) {
try {
Runner runner = runnerForClass(testClass);
if (runner != null) {
configureRunner(runner);
}
return runner;
} catch (Throwable e) {
return new ErrorReportingRunner(testClass, e);
}
}
private void configureRunner(Runner runner) throws InvalidOrderingException {
Description description = runner.getDescription();
OrderWith orderWith = description.getAnnotation(OrderWith.class);
if (orderWith != null) {
Ordering ordering = Ordering.definedBy(orderWith.value(), description);
ordering.apply(runner);
}
}回到JUnitCore
org.junit.runner.JUnitCore#run(org.junit.runner.Request)
运行Runner里的所有测试。先获取Runner
/**
* Run all the tests contained in <code>request</code>.
*
* @param request the request describing tests
* @return a {@link Result} describing the details of the test run and the failed tests.
*/
public Result run(Request request) {
return run(request.getRunner());
}/**
* Do not use. Testing purposes only.
* 运行测试
*/
public Result run(Runner runner) {
// new 结果
Result result = new Result();
// 创建并添加对运行结果的监听,如果listerner不是线程安全的,则使用SynchronizedRunListener装饰
RunListener listener = result.createListener();
notifier.addFirstListener(listener);
try {
// 1. notifier: 测试开始
notifier.fireTestRunStarted(runner.getDescription());
// 2. 开始运行
/*
* Runner = computer
* runners = JUnit 4
* testClass
运行具体的Runner
*/
runner.run(notifier);
// 3. 测试完成
notifier.fireTestRunFinished(result);
} finally {
// 收尾
removeListener(listener);
}
return result;
}- notifier: 测试开始
首先获取测试的描述。Description 可以描述单个测试,也可以是复合的。Description 主要做的事情就是描述这个测试是否被运行。
org.junit.runners.ParentRunner#getDescription
@Override
public Description getDescription() {
Class<?> clazz = getTestClass().getJavaClass();
Description description;
// if subclass overrides `getName()` then we should use it
// to maintain backwards compatibility with JUnit 4.12
// 创建描述
if (clazz == null || !clazz.getName().equals(getName())) {
description = Description.createSuiteDescription(getName(), getRunnerAnnotations());
} else {
description = Description.createSuiteDescription(clazz, getRunnerAnnotations());
}
// 添加Children
for (T child : getFilteredChildren()) {
description.addChild(describeChild(child));
}
return description;
}BlockJUnit4ClassRunner
获取Children
getFilteredChildren ------> org.junit.runners.BlockJUnit4ClassRunner#computeTestMethods
/**
* Returns the methods that run tests. Default implementation returns all
* methods annotated with {@code @Test} on this class and superclasses that
* are not overridden.
*/
protected List<FrameworkMethod> computeTestMethods() {
return getTestClass().getAnnotatedMethods(Test.class);
}org.junit.runners.BlockJUnit4ClassRunner#describeChild
private final ConcurrentMap<FrameworkMethod, Description> methodDescriptions = new ConcurrentHashMap<FrameworkMethod, Description>(); @Override
protected Description describeChild(FrameworkMethod method) {
Description description = methodDescriptions.get(method); // 描述测试方法
if (description == null) {
description = Description.createTestDescription(getTestClass().getJavaClass(),
testName(method), method.getAnnotations());
methodDescriptions.putIfAbsent(method, description);
}
return description;
}RunNotifier
org.junit.runner.notification.RunNotifier#fireTestRunStarted
/**
* Do not invoke.
*/
public void fireTestRunStarted(final Description description) {
new SafeNotifier() {
@Override
protected void notifyListener(RunListener each) throws Exception {
each.testRunStarted(description);
}
}.run();
}void run() {
int capacity = currentListeners.size();
List<RunListener> safeListeners = new ArrayList<RunListener>(capacity);
List<Failure> failures = new ArrayList<Failure>(capacity);
for (RunListener listener : currentListeners) {
try {
// 提醒所有Listner,测试开始,例如org.junit.runner.Result.Listener 对测试进行计时
notifyListener(listener);
safeListeners.add(listener);
} catch (Exception e) {
failures.add(new Failure(Description.TEST_MECHANISM, e));
}
}
fireTestFailures(safeListeners, failures);
}ParentRunner
开始运行测试,这里的实现是 ParentRunner
org.junit.runners.ParentRunner#run
runner.run(notifier);
@Override
public void run(final RunNotifier notifier) {
EachTestNotifier testNotifier = new EachTestNotifier(notifier,
getDescription());
// 提醒测试即将开始
testNotifier.fireTestSuiteStarted();
try {
// 根据获取的statement,运行测试方法。例如RunBefores 会运行before的方法。InvokeMethod则是具体测试方法执行的Statment
Statement statement = classBlock(notifier);
statement.evaluate();
// 下面是对不同情况的处理。
} catch (AssumptionViolatedException e) { // 对假设失败的处理,如果假设是错误的,则不往下执行
testNotifier.addFailedAssumption(e);
} catch (StoppedByUserException e) {
throw e;
} catch (Throwable e) { // 捕获所有异常
testNotifier.addFailure(e);
} finally {
// 测试完成,提醒Notifier
testNotifier.fireTestSuiteFinished();
}
}org.junit.runners.ParentRunner#classBlock
构建一个Statement来运行测试类里的所有测试
- 使用getChildren来获取要运行的children
- 如果在过滤和忽略后仍有遗漏的children,构建一个Statement
- 在测试类和超类上应用所有ClassRule
- 在测试类和超类上运行所有未被覆盖的@BeforeClass方法,如果抛出异常,停止执行并传递Exception
- 在test-class上运行所有剩余的测试
- 在测试类和超类上运行所有未被覆盖的@AfterClass方法,如果抛出异常,停止执行并传递Exception
protected Statement classBlock(final RunNotifier notifier) {
Statement statement = childrenInvoker(notifier);
if (!areAllChildrenIgnored()) {
// 针对不同情况对该Statement进行修饰
statement = withBeforeClasses(statement);
statement = withAfterClasses(statement);
statement = withClassRules(statement);
statement = withInterruptIsolation(statement);
}
return statement;
}BlockJUnit4ClassRunnerWithParameters的实现 org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters#classBlock
@Override
protected Statement classBlock(RunNotifier notifier) {
Statement statement = childrenInvoker(notifier);
statement = withBeforeParams(statement);
statement = withAfterParams(statement);
return statement;
}接下来分析 ParentRunner的实现
org.junit.runners.ParentRunner#childrenInvoker
/**
* Returns a {@link Statement}: Call {@link #runChild(Object, RunNotifier)}
* on each object returned by {@link #getChildren()} (subject to any imposed
* filter and sort)
*/
protected Statement childrenInvoker(final RunNotifier notifier) {
// 返回一个Statment
return new Statement() {
@Override
public void evaluate() {
runChildren(notifier);
}
};
}org.junit.runners.ParentRunner#runChildren
private void runChildren(final RunNotifier notifier) {
final RunnerScheduler currentScheduler = scheduler;
try {
// 遍历运行类里面每一个测试
for (final T each : getFilteredChildren()) {
currentScheduler.schedule(new Runnable() {
public void run() {
// 注意这个方法默认实现 : org.junit.runners.BlockJUnit4ClassRunner#runChild
ParentRunner.this.runChild(each, notifier);
}
});
}
} finally {
currentScheduler.finished();
}
}org.junit.runners.ParentRunner#getFilteredChildren
这里的getChildren() 是获取测试方法的地方
private List<T> getFilteredChildren() {
if (filteredChildren == null) {
childrenLock.lock();
try {
if (filteredChildren == null) {
filteredChildren = Collections.unmodifiableList(
new ArrayList<T>(getChildren()));
}
} finally {
childrenLock.unlock();
}
}
return filteredChildren;
}org.junit.runners.BlockJUnit4ClassRunner#getChildren
@Test 注解方法
@Override
protected List<FrameworkMethod> getChildren() {
return computeTestMethods();
}
/**
* Returns the methods that run tests. Default implementation returns all
* methods annotated with {@code @Test} on this class and superclasses that
* are not overridden.
*/
protected List<FrameworkMethod> computeTestMethods() {
// 获取被 @Test 注解修饰的方法列表。范围:未被覆写的方法与父类的方法
return getTestClass().getAnnotatedMethods(Test.class);
}BlockJUnit4ClassRunner 覆写的 rg.junit.runners.BlockJUnit4ClassRunner#runChild
/**
* Runs the test corresponding to {@code child}, which can be assumed to be
* an element of the list returned by {@link ParentRunner#getChildren()}.
* Subclasses are responsible for making sure that relevant test events are
* reported through {@code notifier}
*/
@Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
// 获取Decription
Description description = describeChild(method);
// 是否有 @Ignored 注解
if (isIgnored(method)) {
notifier.fireTestIgnored(description);
} else {
// 新建Statement
Statement statement = new Statement() {
@Override
public void evaluate() throws Throwable {
methodBlock(method).evaluate();
}
};
runLeaf(statement, description, notifier);
}
}org.junit.runners.BlockJUnit4ClassRunner#describeChild
/**
* Returns a {@link Description} for {@code child}, which can be assumed to
* be an element of the list returned by {@link ParentRunner#getChildren()}
*/
@Override
protected Description describeChild(FrameworkMethod method) {
// 获取方法描述
Description description = methodDescriptions.get(method);
// 如果为空则新建一个
if (description == null) {
description = Description.createTestDescription(getTestClass().getJavaClass(),
testName(method), method.getAnnotations());
methodDescriptions.putIfAbsent(method, description);
}
return description;
}org.junit.runner.Description#Description(java.lang.Class<?>, java.lang.String, java.io.Serializable, java.lang.annotation.Annotation...)
/** * Create a <code>Description</code> of a single test named <code>name</code> in the class <code>clazz</code>. * Generally, this will be a leaf <code>Description</code>. * * @param clazz the class of the test * @param name the name of the test (a method name for test annotated with {@link org.junit.Test}) * @param annotations meta-data about the test, for downstream interpreters * @return a <code>Description</code> named <code>name</code> */ public static Description createTestDescription(Class<?> clazz, String name, Annotation... annotations) { return new Description(clazz, formatDisplayName(name, clazz.getName()), annotations); }
org.junit.runners.BlockJUnit4ClassRunner#methodBlock
和 org.junit.runners.ParentRunner#classBlock 有点像,一个是类级别,一个是方法级别
默认实现:
- 执行createTest返回的方法
- 但是,如果@Test注解有exptected,抛出相同类型的异常,正常返回,否则抛出异常
- 如果有时间限制,则超时抛出异常
- 总是先运行在该类和父类中未被覆盖的@Before方法,如果抛出异常,停止执行并且传递下去
- 总是先运行在该类和父类中未被覆盖的@After方法,所有After方法总会被执行。抛出的异常会被合并成MultipleFailureException
- 允许Rule修改上面的执行。Rule可以阻止上面执行,也可以在运行前后添加额外的行为,或者修改异常抛出的方式
/**
* Returns a Statement that, when executed, either returns normally if
* {@code method} passes, or throws an exception if {@code method} fails.
*
* Here is an outline of the default implementation:
*
* <ul>
* <li>Invoke {@code method} on the result of {@link #createTest(org.junit.runners.model.FrameworkMethod)}, and
* throw any exceptions thrown by either operation.
* <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@link Test#expected()}
* attribute, return normally only if the previous step threw an
* exception of the correct type, and throw an exception otherwise.
* <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
* timeout} attribute, throw an exception if the previous step takes more
* than the specified number of milliseconds.
* <li>ALWAYS run all non-overridden {@code @Before} methods on this class
* and superclasses before any of the previous steps; if any throws an
* Exception, stop execution and pass the exception on.
* <li>ALWAYS run all non-overridden {@code @After} methods on this class
* and superclasses after any of the previous steps; all After methods are
* always executed: exceptions thrown by previous steps are combined, if
* necessary, with exceptions from After methods into a
* {@link MultipleFailureException}.
* <li>ALWAYS allow {@code @Rule} fields to modify the execution of the
* above steps. A {@code Rule} may prevent all execution of the above steps,
* or add additional behavior before and after, or modify thrown exceptions.
* For more information, see {@link TestRule}
* </ul>
*
* This can be overridden in subclasses, either by overriding this method,
* or the implementations creating each sub-statement.
*/
protected Statement methodBlock(final FrameworkMethod method) {
Object test;
try {
test = new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest(method);
}
}.run(); // run() 实际上是调用了runReflectiveCall()
} catch (Throwable e) {
return new Fail(e);
}
Statement statement = methodInvoker(method, test); // 使用反射执行测试方法
statement = possiblyExpectingExceptions(method, test, statement);
statement = withPotentialTimeout(method, test, statement);
statement = withBefores(method, test, statement);
statement = withAfters(method, test, statement);
statement = withRules(method, test, statement);
statement = withInterruptIsolation(statement);
return statement;
}
/**
* Returns a new fixture for running a test. Default implementation executes
* the test class's no-argument constructor (validation should have ensured
* one exists).
*/
protected Object createTest() throws Exception {
// 获取测试类 --> 获取唯一public的构造器 --> 获取实例
return getTestClass().getOnlyConstructor().newInstance();
}org.junit.runners.ParentRunner#runLeaf
运行单个测试(叶子), 和run有点像,不过run是类级别的
/**
* Runs a {@link Statement} that represents a leaf (aka atomic) test.
*/
protected final void runLeaf(Statement statement, Description description,
RunNotifier notifier) {
// 每个测试的Notifier,传递测试的Description。接收Throwable
EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
eachNotifier.fireTestStarted();
try {
statement.evaluate();
} catch (AssumptionViolatedException e) { // 违反了假设
eachNotifier.addFailedAssumption(e);
} catch (Throwable e) {
eachNotifier.addFailure(e); // 抛出异常
} finally {
eachNotifier.fireTestFinished(); // 测试完成
}
}