junit源码学习-交织的TestCase和TestResult

来源:互联网 发布:手机版淘宝网首页 编辑:程序博客网 时间:2024/03/29 16:39

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的执行方法,匠心独运啊......

原创粉丝点击