前言
写完程序后,总要以前我总喜欢写个main函数测试下。但随着时间推移和程序变大。main显的不那边方法。此时,Junit测试框架顺应而生。
应用场景
Junit的测试被多大框架所集成:
- idea开发工具
- maven test
- …
它们通过组件的方式最终触发如下代码
//JunitTest即包含测试方法的类
Result result = JUnitCore.runClasses(JunitTest.class);
//对于执行失败的情况打印失败信息
//idea把失信信息界面画给我们看
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
//测试类
public class JunitTest {
@Test
public void yoyo(){
System.out.println("yoyo");
assertTrue("eeee", false);
}
}
核心组成分析
以下是Junit4的核心类
Statement
表示运代码段接口,可以执行的代码。
- InvokeMethod 其基本实现,表示如例子上面的@Test函数
- ExpectException @Test中expected异常处理,是否符合
- FailOnTimeout @Test中timeout超时处理
- RunBefores 表示方法执行前,@Before, BeforeClass
- RunAfters 表示方法执行后 @After,AfterClass
- RunRules表示方法的执行规则@Rule,ClassRule
通过以上方式并结合《设计模式—装饰模式》直接把代码串成一块。
Runner
测试用例的执行者。Statement的组织者。
- ParentRunner拆解单个测试类,并实现过滤基本功能
- Suite,ParentRunner实现之一,它可以包含多个类来进行测试执行。@SuiteClasses
- BlockJUnit4ClassRunner,ParentRunner实现之一。利用过滤功能实现@IncludeCategory,@ExcludeCategory,@Category,@Ignore
RunnerBuilder
用来给测试类创建runner,根据不同的测试类,可以创建不同的Runner,从而实现多版本支持。如BlockJUnit4ClassRunner,JUnit38ClassRunner支持。多功能支持如BlockJUnit4ClassRunnerWithParameters。spring Test也是靠这个实现的。
- IgnoredBuilder 支持@Ignore的Runner
- AnnotatedBuilder 支持@RunWith的
- SuiteMethodBuilder 简单的方法执行
- JUnit3Builder 支持Junit3版本
- junit4Builder 支持Junit4版本
- AllDefaultPossibilitiesBuilder 自动判断类,根据你编写类的不同风格,自动为选择Builder
通过以上方式并结合《设计模式—建造者模式》直接根据不同的Class定义创建不同的Runner
Request
根据对不同结果的期望,对请求进行区分。
1.ClassRequest 根据class转用Runner
2. FilterRequest 过滤掉一些Runner
3. SortingRequest 排序
通过以上方式并结合《设计模式—命令模式》,根据期望的不同结果,下不同的命令
Computer
建立Builder与真实Class联系。
执行流程分析
- 通过一切方式传入测试类
JUnitCoure.runClasses(Class<?>... classes){
return runClasses(defaultComputer(), classes);
}
- 创建Request对象
public Result run(Computer computer, Class<?>... classes) {
return run(Request.classes(computer, classes));
}
public static Request classes(Computer computer, Class<?>... classes) {
try {
//创建Builder策略
AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
//computer使用之前AllDefaultPossibilitiesBuilder解析传进来Class转化为Runner
//这里使用了一个代理Runner,延迟执行
Runner suite = computer.getSuite(builder, classes);
//使用最简单的Request策略
return runner(suite);
} catch (InitializationError e) {
return runner(new ErrorReportingRunner(e, classes));
}
}
- 获取真正的Runner
//调用run(Runner runner)方法其中的runner参数是通过Request的getRunner()方法得来的。
//由2中的代码可知,这里得出的runner是从Computer#getSuite方法中得出的,该方法最终调用到的是传入的AllDefaultPossibilitiesBuilder,通过他的runnerForClass方法返回出真正的runner
public Runner runnerForClass(Class<?> testClass) throws Throwable {
//所有的Builde
List<RunnerBuilder> builders = Arrays.asList(
ignoredBuilder(),
annotatedBuilder(),
suiteMethodBuilder(),
junit3Builder(),
junit4Builder());
//循环解析Class,然后判断其适合哪个Builder
//junit4Builder为我们创造BlockJUnit4ClassRunner
for (RunnerBuilder each : builders) {
Runner runner = each.safeRunnerForClass(testClass);
if (runner != null) {
return runner;
}
}
return null;
}
- 方法真正被run的入口
public Result run(Runner runner) {
//结果类
Result result = new Result();
RunListener listener = result.createListener();
//notifier是监听者,加入监听器,绑定结果集
notifier.addFirstListener(listener);
try {
notifier.fireTestRunStarted(runner.getDescription());
//执行Runner
runner.run(notifier);
notifier.fireTestRunFinished(result);
} finally {
removeListener(listener);
}
return result;
}
public void run(final RunNotifier notifier) {
EachTestNotifier testNotifier = new EachTestNotifier(notifier,
getDescription());
testNotifier.fireTestSuiteStarted();
try {
//1.如果是Suite Runner则会循环调用下面的所有Runner
//2.加载Class等级的方法,
Statement statement = classBlock(notifier);
//先调用本类的Class等级的方法,再执行子Runner
statement.evaluate();
} catch (AssumptionViolatedException e) {
testNotifier.addFailedAssumption(e);
} catch (StoppedByUserException e) {
throw e;
} catch (Throwable e) {
testNotifier.addFailure(e);
} finally {
testNotifier.fireTestSuiteFinished();
}
}
- 获取所有需要被执行的测试方法
protected Statement classBlock(final RunNotifier notifier) {
//子集合Runner执行代码
Statement statement = childrenInvoker(notifier);
if (!areAllChildrenIgnored()) {
statement = withBeforeClasses(statement);
statement = withAfterClasses(statement);
statement = withClassRules(statement);
statement = withInterruptIsolation(statement);
}
return statement;
}
- 方法最终被执行
@Override
//当Runner是BlockJUnit4ClassRunner
//其子执行体如下
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description = describeChild(method);
if (isIgnored(method)) {
notifier.fireTestIgnored(description);
} else {
Statement statement;
try {
//拼装@Before,@After之类的注解
statement = methodBlock(method);
}
catch (Throwable ex) {
statement = new Fail(ex);
}
runLeaf(statement, description, notifier);
}
}
主要参考
《从执行流程分析Junit4源码》
《官方文档》
《Junit4框架分析报告》
《JUnit4学习笔记(四):利用Rule扩展JUnit》