androidjunit测试报告,关于使⽤JUnit4测试Android程序的⼀点
分析
JUnit 4是⼀种流⾏的Java单元测试⼯具。使⽤JUnit 4和st包可以为Android程序编写单元测试和Instrument测试。下⾯我对JUnit 4在Android程序上的使⽤做⼀些简单的分析。
JUnit 4中的3个核⼼概念
⾸先简单介绍⼀下JUnit 4。JUnit 4是⼀个单元测试框架,简单来说,JUnit 4的⼯作就是运⾏单元测试,然后将结果展⽰给⽤户。在这个过程中涉及3个核⼼概念:表⽰单元测试的Statement、运⾏单元测试的Runner,以及接收测试数据,展⽰结果的RunNotifier。
下⾯的代码摘录⾃org.junit.runners.ParentRunner,这段代码很好的展⽰了3个概念之间的关系:Runner运⾏Statement,将结果通知给RunNotifier。
@Override
public void run(final RunNotifier notifier) {
EachTestNotifier testNotifier = new EachTestNotifier(notifier, getDescription());
try {
丽丽英语
Statement statement = classBlock(notifier);
听力课堂
statement.evaluate();
} catch (AssumptionViolatedException e) {
testNotifier.addFailedAssumption(e);
} catch (StoppedByUrException e) {
纸牌屋 第二季throw e;
} catch (Throwable e) {
testNotifier.addFailure(e);
}
}
Runner
org.junit.runner.Runner是⼀个抽象类,拥有两个抽象⽅法:
public abstract Description getDescription();
public abstract void run(RunNotifier notifier);会计证年检时间
通常我们不会直接接触到Runner类。如果想⾃⼰编写Runner,可以从org.junit.runners.BlockJUnit4ClassRunner派⽣。
BlockJUnit4ClassRunner也是JUnit 4默认的Runner类。如果不想使⽤默认Runner,可以在测试类上添加注解@RunWith,设置测试需要的Runner。在测试Android程序时,通常会加上
@RunWith(AndroidJUnit4::class)
这⾏代码会使⽤junit.runners.AndroidJUnit4作为Runner。AndroidJUnit4是⼀个包装类,它会检查系统属性
java.runtime.name,如果其中包含字符串android,AndroidJUnit4会使⽤
这⾥说⼀下@RunWith⽣效的过程。在运⾏测试的时候(⽐如Gradle使⽤的
ignoredBuilder
annotatedBuilder
suiteMethodBuilder
junit3Builder
junit4Builder
逐⼀寻找合适的RunnerBuilder,然后构建出Runner。听起来很复杂,实际上只需两⾏代码:
final Request request = Request.aClass((Class)testClass);
final Runner runner = Runner();
Statement
Statement表⽰⼀个测试⽤例,测试⽤例只有⼀个动作:执⾏。因此Statement类⾮常简单:
public abstract class Statement {
public abstract void evaluate() throws Throwable;
}
如果测试失败,Statement抛出异常。如果执⾏成功,no news is good news。Statement虽然简洁,通过组合可以构造出⾮常丰富的⽤法。⽐如下⾯这段代码
protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) {
List befores = getTestClass().getAnnotatedMethods(Before.class);
return befores.isEmpty() ? statement : new RunBefores(statement, befores, target);
}
这段代码摘⾃BlockJUnit4ClassRunner类。熟悉JUnit 4的⼈⼀看就会明⽩,这是处理@Before注解的地⽅。实际上,根据这段代码,⼤家应该就可以猜出RunBefores类⼤致是什么样⼦了。为了例⼦的完整性,我们把RunBefores类的代码也展⽰在这⾥。
package org.junit.internal.runners.statements;
import java.util.List;
import org.del.FrameworkMethod;
import org.del.Statement;
public class RunBefores extends Statement {
private final Statement next;
private final Object target;
private final List befores;
public RunBefores(Statement next, List befores, Object target) {
< = next;
this.befores = befores;
东南方英语this.target = target;
}
@Override
public void evaluate() throws Throwable {
cgi是什么for (FrameworkMethod before : befores) {
before.invokeExplosively(target);
}
next.evaluate();
}
}
如同上⾯的@Before例⼦,JUnit 4的很多特性都是通过Statement组合实现的。下⾯这段代码也是BlockJUnit4ClassRunner的⼀部分,从这⾥我们可以看出,@Rule也是通过封装Statement实现的。
protected Statement methodBlock(FrameworkMethod method) {
Object test;
try {
test = new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest();
}
}.run();
} 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);
return statement;
}
为了⽅便读者参考,我们把org.junit.rules.TestRule接⼝和RunRules类贴在这⾥。
public interface TestRule {
Statement apply(Statement ba, Description description);
}
package org.junit.rules;
import org.junit.runner.Description;
import org.del.Statement;
public class RunRules extends Statement {
private final Statement statement;
public RunRules(Statement ba, Iterable rules, Description description) {
statement = applyAll(ba, rules, description);
intensive}
@Override
public void evaluate() throws Throwable {
statement.evaluate();
}
private static Statement applyAll(Statement result, Iterable rules,
Description description) {
for (TestRule each : rules) {
result = each.apply(result, description);
}
return result;
}
}
@BeforeClass注解也使⽤了Statement,下⾯的代码来⾃ParentRunner。ParentRunner是BlockJUnit4ClassRunner的⽗类。protected Statement classBlock(final RunNotifier notifier) {
Statement statement = childrenInvoker(notifier);
if (!areAllChildrenIgnored()) {
statement = withBeforeClass(statement);
statement = withAfterClass(statement);
statement = withClassRules(statement);
}
return statement;
}
RunNotifier
Runner负责执⾏测试⽤例,测试的结果通知给RunNotifier。RunNotifier是⼀个RunListener集合,测试信息最终由RunListener处理。我们运⾏单元测试时,控制台输出的信息就是由TextListener⽣成的。
public class RunListener {
public void testRunStarted(Description description) throws Exception {}
public void testRunFinished(Result result) throws Exception {}
public void testStarted(Description description) throws Exception {}
public void testFinished(Description description) throws Exception {}
public void testFailure(Failure failure) throws Exception {}
public void testAssumptionFailure(Failure failure) {}
public void testIgnored(Description description) throws Exception {}
}
在运⾏测试时,ParentRunner负责将测试事件通知给RunNotifier。
protected final void runLeaf(Statement statement, Description description, RunNotifier notifier) {
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();
}
}
protests
ParentRunner和BlockJUnit4ClassRunner
在前⾯介绍Runner、Statement和RunNotifier时,已经不⽌⼀次提到了BlockJUnit4ClassRunner和ParentRunner。这是两个⾮常重要的类,有了前⾯的基础,在这⾥我们可以做⼀些深⼊分析。
ParentRunner是⼀个抽象类,它有两个重要的函数:run和runLeaf,这两个函数在前⾯已经介绍过了。ParentRunner还有两个重要接⼝:
protected abstract void runChild(T child, RunNotifier notifier);
protected abstract List getChildren();
runChild负责执⾏⼀个测试⽅法。正如⽅法classBlock所暗⽰的,ParentRunner运⾏⼀个测试类。ParentRunner将⼀组测试⽅法看作⾃⼰的child。child通过getChildren获得。ParentRunner将各个child所代表的测试⽤例通过childrenInvoker封装成⼀个Statement,在加上@BeforeClass和@AfterClass,构造成最终的Statement。
@Override