TestCase中实例化了TestResult,并将自身化为参数调用TestResult的run方法
TestCase.java
/**
* Creates a default TestResult object
* 生成TestResult实例,单独开方法的原因我猜是有可能使用者自己继承或者实现TestCase和TestResult,
* 那么直接重写这个方法就行了
* @see TestResult
*/
protected TestResult createResult() {
return new TestResult();
}
/**
* A convenience method to run this test, collecting the results with a
* default TestResult object.
* 获取到TestResult实例并传入下面的run方法中
* @see TestResult
*/
public TestResult run() {
TestResult result= createResult();
run(result);
return result;
}
/**
* 调用TestResult的run方法,并把自身(TestCase)作为参数传给方法
* Runs the test case and collects the results in TestResult.
*/
public void run(TestResult result) {
result.run(this);
}
执行过程进入TestResult中
TestResult.java
/**
* 运行testCase
* Runs a TestCase.
*/
protected void run(final TestCase test) {
startTest(test);
Protectable p= new Protectable() {//Protectable的一个匿名实现
public void protect() throws Throwable {
test.runBare();//定义为调用TestCase的runBare方法
}
};
runProtected(test, p);
endTest(test);
}
/**
* Runs a TestCase.
*/
public void runProtected(final Test test, Protectable p) {
try {
p.protect();//实际调用TestCase的runBare方法
}
catch (AssertionFailedError e) {
addFailure(test, e);
}
catch (ThreadDeath e) { // don't catch ThreadDeath by accident
throw e;
}
catch (Throwable e) {
addError(test, e);
}
}
很好,我们又回到了TestCase
TestCase.java
/**
* Runs the bare test sequence.
* @throws Throwable if any exception is thrown
* 加上了环绕方法setUp()和tearDown(),调用runTest()
*/
public void runBare() throws Throwable {
Throwable exception= null;
setUp();
try {
runTest();
} catch (Throwable running) {
exception= running;
}
finally {
try {
tearDown();
} catch (Throwable tearingDown) {
if (exception == null) exception= tearingDown;
}
}
if (exception != null) throw exception;
}
/**
* Override to run the test and assert its state.
* @throws Throwable if any exception is thrown
* 实际执行测试方法
*/
protected void runTest() throws Throwable {
assertNotNull("TestCase.fName cannot be null", fName); // Some VMs crash when calling getMethod(null,null);
Method runMethod= null;
try {
// use getMethod to get all public inherited
// methods. getDeclaredMethods returns all
// methods of this class but excludes the
// inherited ones.
runMethod= getClass().getMethod(fName, (Class[])null);//获取实际待执行方法
} catch (NoSuchMethodException e) {
fail("Method /""+fName+"/" not found");
}
if (!Modifier.isPublic(runMethod.getModifiers())) {//判断方法作用域
fail("Method /""+fName+"/" should be public");
}
try {
runMethod.invoke(this);//实际执行方法
}
catch (InvocationTargetException e) {
e.fillInStackTrace();
throw e.getTargetException();
}
catch (IllegalAccessException e) {
e.fillInStackTrace();
throw e;
}
}
终于执行了实际的测试方法,那么,接下来的问题是:为什么执行测试方法要如此在TestCase和TestResult之间交织进行呢?
能不能都在TestCase里面执行呢?
我注意到几个现象,一是在TestResult中Protectable接口的匿名实现;二是所有的异常处理都是以listener的观察者形式注册到TestResult中,而环绕方法setUp()和tearDown()则是定义在TestCase中等待具体实现;三是方法执行次数标识int fRunTests和方法执行停止标识boolean fStop都在TestResult中。
首先,针对第一点现象,TestResult中为什么不直接调用TestCase的runBare()方法,而是采用了Protectable接口的匿名实现的方式来调用TestCase的runBare()方法?Protectable接口名为保护接口,它究竟保护了什么?
这时候,我注意到TestResult中的run(final TestCase test)方法的访问域是protected,而runProtected(final Test test, Protectable p)方法的访问域是public,换句话说,如果我们需要自己对TestResult进行扩展时,我们只需重写run(final TestCase test)方法,而不改变外部对runProtected(final Test test, Protectable p)方法的调用,整体对外提供的API不会有任何改变。设计原则之对修改封闭,对扩展开放。那么,Protectable接口保护的是什么就昭然若揭了,它保护的是原实现细节,只对外提供接口。
针对第二和第三现象,我所想到的是类的单一职责原则。TestCase类拥有一个方法执行结果对象TestResult和方法执行方法run(),runBare(),runTest(),它的职责就是执行测试方法以及其环绕方法;而TestResult类拥有三个list,分别为fFailures、fErrors、fListeners,前两者用于存储执行结果的assertFailure和exception,后者监听到assertFailure和exception后提供给容器;同时还拥有执行次数标识int fRunTests和方法执行停止标识boolean fStop,以上这些都是执行结果的具现化表征,换句话说,只有执行出结果,这些东西才会产生有效值,职责归属于TestResult无可厚非。然后执行方法本身是在TestCase中,所以,执行过程一定要在TestResult中进行,才能获取对应的result对以上属性产生影响。
而为了使TestCase和TestResult容易扩展,在TestCase中实例化TestResult的方法被单独提取出,作用域为protected,可进行扩展;而在TestResult中采用了Protectable接口的匿名实现,避免直接调用TestCase的执行方法,匠心独运啊......