133.3结果报告 TestResult 如果一个 Test case在森林中运行,是否有人关心其结果呢?当然一你之所以运行测试 就是为了要证实它们能够运行。测试运行完后,你想要一个记录,一个什么能够工作 和什么未能工作的总结 如果测试具有相等的成功或失败的机会,或者如果我们刚刚运行一个测试,我们可能 只是在 Test Case对象中设定一个标志,并且当测试完毕时去看这个标志。然而,测试 (往往)是非常不均匀的一他们通常都会工作。因此我们只是想要记录失败,以及对 成功的一个高度浓缩的总结 The Smalltalk Best Practice Patterns (the Beck, K. Smalltalk Best Practice Patterns Prentice hall1996)有一个可以适用的模式,称为 Collecting Parameter(收集参数) 其建议当你需要在多个方法间进行结果收集时,应该在方法中增加一个参数,并传递 个对象来为你收集结果。我们创建一个新的对象, TestResult(测试结果),来收集 运行的测试的结果。 public class TestResult extends Object protected int fRun Tests; public TestResult(t 这个简单版本的 TestResult仅仅能够计算所运行测试的数目。为了使用它,我们不得 不在 Test case. run0)方法中添加一个参数,并通知 TestResult该测试正在运行 public void run(TestResult result)( result. startTest(this) setUp(; runTest( 并且 TestResult必须要记住所运行测试的数目: public synchronized void startTest(Test test)( fRun Tests++ 我们将 TestResult的 stratTest方法声明为 synchronized,从而当测试运行在不同的线 程中时,一个单独的 TestResult能够安全地对结果进行收集。最后,我们想要保持 Test case简单的外部接口,因此创建一个无参的run(版本,其负责创建自己的 Test resu|t。 public TestResult run(t TestResult result= createResult( run(result); return result:
1.3.3.3 结果报告-TestResult 如果一个 TestCase 在森林中运行,是否有人关心其结果呢?当然-你之所以运行测试 就是为了要证实它们能够运行。测试运行完后,你想要一个记录,一个什么能够工作 和什么未能工作的总结。 如果测试具有相等的成功或失败的机会,或者如果我们刚刚运行一个测试,我们可能 只是在 TestCase 对象中设定一个标志,并且当测试完毕时去看这个标志。然而,测试 (往往)是非常不均匀的-他们通常都会工作。因此我们只是想要记录失败,以及对 成功的一个高度浓缩的总结。 The Smalltalk Best Practice Patterns(请参见 Beck, K. Smalltalk Best Practice Patterns, Prentice Hall, 1996)有一个可以适用的模式,称为 Collecting Parameter(收集参数)。 其建议当你需要在多个方法间进行结果收集时,应该在方法中增加一个参数,并传递 一个对象来为你收集结果。我们创建一个新的对象,TestResult(测试结果),来收集 运行的测试的结果。 public class TestResult extends Object { protected int fRunTests; public TestResult() { fRunTests= 0; } } 这个简单版本的 TestResult 仅仅能够计算所运行测试的数目。为了使用它,我们不得 不在 TestCase.run()方法中添加一个参数,并通知 TestResult 该测试正在运行: public void run(TestResult result) { result.startTest(this); setUp(); runTest(); tearDown(); } 并且 TestResult 必须要记住所运行测试的数目: public synchronized void startTest(Test test) { fRunTests++; } 我们将 TestResult 的 stratTest 方法声明为 synchronized,从而当测试运行在不同的线 程中时,一个单独的 TestResult 能够安全地对结果进行收集。最后,我们想要保持 TestCase 简单的外部接口,因此创建一个无参的 run()版本,其负责创建自己的 TestResult。 public TestResult run() { TestResult result= createResult(); run(result); return result;
protected TestResult create Result(i return new TestResult(; 我们下面的设计快照可如图3所示 Testcase TestEs ult run(TestResult runEs Collecting Parameter arDo 图3 TestResult应用 Collecting Parameter 如果测试总是能够正确运行,那么我们将没有必要编写它们。只有当测试失败时测试 才是让人感兴趣的,尤其是当我们没有预期到它们会失败的时候。更有甚者,测试能 够以我们所预期的方式失败,例如通过计算一个不正确的结果;或者它们能够以更加 吸引人的方式失败,例如通过编写一个数组越界。无论测试怎样失败,我们都想执行 后面的测试 JUnit区分了失败( failures)和错误( errors)。失败的可能性是可预期的,并且以使 用断言( assertion)来进行检查。而错误则是不可预期的问题,如 ArraylndexOutOfBounds Exception。失败可通过一个 Assertion Failed Error来发送。为了 能够识别出一个不可预期的错误和一个失败,将在 catch子句(1)中对失败进行捕获。 子句(2)则捕获所有其它的异常,并确保我们的测试能够继续运行 public void run(TestResult result) result.startTest(this) unRest( catch(Assertion Failed Error e)[/1 result. addFailure(this, e) catch(Throwable e)( ∥/2 result. addError(this, e); tear Down( TestCase提供的aset方法会触发一个 Assertion failed Error。儿uUnt针对不同的目的提 供一组 assert方法。下面只是最简单的一个:
} protected TestResult createResult() { return new TestResult(); } 我们下面的设计快照可如图 3 所示。 图 3 TestResult 应用 Collecting Parameter 如果测试总是能够正确运行,那么我们将没有必要编写它们。只有当测试失败时测试 才是让人感兴趣的,尤其是当我们没有预期到它们会失败的时候。更有甚者,测试能 够以我们所预期的方式失败,例如通过计算一个不正确的结果;或者它们能够以更加 吸引人的方式失败,例如通过编写一个数组越界。无论测试怎样失败,我们都想执行 后面的测试。 JUnit 区分了失败(failures)和错误(errors)。失败的可能性是可预期的,并且以使 用断言( assertion )来进行检查。而错误则是不可预期的问题,如 ArrayIndexOutOfBoundsException。失败可通过一个 AssertionFailedError 来发送。为了 能够识别出一个不可预期的错误和一个失败,将在 catch 子句(1)中对失败进行捕获。 子句(2)则捕获所有其它的异常,并确保我们的测试能够继续运行... public void run(TestResult result) { result.startTest(this); setUp(); try { runTest(); } catch (AssertionFailedError e) { //1 result.addFailure(this, e); } catch (Throwable e) { // 2 result.addError(this, e); } finally { tearDown(); } } TestCase 提供的 assert 方法会触发一个 AssertionFailedError。JUnit 针对不同的目的提 供一组 assert 方法。下面只是最简单的一个:
protected void assert(boolean condition)( if(Condition) throw new Assertion Failed Error(; Assertion Failed Error不应该由客户( Testcase中的一个测试方法)来负责捕获,而应 该由 Template Method内部的 Test case . run)来负责。因此我们将 Assertion Failed error 派生自Eror public class Assertion Failed Error extends Error public Assertion FailedError Ot 在 TestResult中收集错误的方法可如下所示 public synchronized void add error(Test test throwable t) fErrors. addElement(new Test Failure(test, t)) public synchronized void addFailure(Testtest, Throwable t)( fAilures. add Element(new TestFailure(test t) Test Failure是一个小的框架内部帮助类( helper class,其将失败的测试和为后续报告 发送信号的异常绑定在一起。 public class TestFailure extends Object protected Test fAiled Test; protected Throwable fThrownException 规范形式的 Collecting parameter模式要求我们将 Collecting parameter传递给每一个方 法。如果我们遵循该建议,每一个测试方法都将需要 TestResult的参数。其将会造成 这些方法签名( signature)的“污染”。使用异常来发送失败可以作为一个友善的副 作用,使我们能够避免这种签名的污染。一个测试案例方法,或一个其所调用的帮助 方法( helper method),可在不必知道 TestResult的情况下抛出一个异常。作为一个 进修材料,这里给出一个简单的测试方法,其来自于我们 Money Test套件。其演示了 个测试方法是如何不必知道任何关于 TestResult的信息的。 public void testMoney Equals(( assert(.equals(null)); assertEquals(f12CHF, f12CHF) assert Equals(f12CHE, new Money (12,"CHF ) assert(!.equals(f14CHF); JUnit提出了关于 TestResult的不同实现。其缺省实现是对失败和错误的数目进行计数 并收集结果。 TextTest Result收集结果并以一种文本的形式来表达它们。最后, JUnit Test Runner的图形版本则使用 UITestResult来更新图形化的测试状态。 Test Result是框架的一个扩展点( extension point)。客户能够自定义它们的 TestResult 类,例如 HTMLTestResult可将结果上报为一个HTML文档
protected void assert(boolean condition) { if (!condition) throw new AssertionFailedError(); } AssertionFailedError 不应该由客户(TestCase 中的一个测试方法)来负责捕获,而应 该由 Template Method 内部的 TestCase.run()来负责。因此我们将 AssertionFailedError 派生自 Error。 public class AssertionFailedError extends Error { public AssertionFailedError () {} } 在 TestResult 中收集错误的方法可如下所示: public synchronized void addError(Test test, Throwable t) { fErrors.addElement(new TestFailure(test, t)); } public synchronized void addFailure(Test test, Throwable t) { fFailures.addElement(new TestFailure(test, t)); } TestFailure 是一个小的框架内部帮助类(helper class),其将失败的测试和为后续报告 发送信号的异常绑定在一起。 public class TestFailure extends Object { protected Test fFailedTest; protected Throwable fThrownException; } 规范形式的Collecting parameter模式要求我们将Collecting parameter传递给每一个方 法。如果我们遵循该建议,每一个测试方法都将需要 TestResult 的参数。其将会造成 这些方法签名(signature)的“污染”。使用异常来发送失败可以作为一个友善的副 作用,使我们能够避免这种签名的污染。一个测试案例方法,或一个其所调用的帮助 方法(helper method),可在不必知道 TestResult 的情况下抛出一个异常。作为一个 进修材料,这里给出一个简单的测试方法,其来自于我们 MoneyTest 套件。其演示了 一个测试方法是如何不必知道任何关于 TestResult 的信息的。 public void testMoneyEquals() { assert(!f12CHF.equals(null)); assertEquals(f12CHF, f12CHF); assertEquals(f12CHF, new Money(12, "CHF")); assert(!f12CHF.equals(f14CHF)); } JUnit 提出了关于 TestResult 的不同实现。其缺省实现是对失败和错误的数目进行计数 并收集结果。TextTestResult 收集结果并以一种文本的形式来表达它们。最后,JUnit Test Runner 的图形版本则使用 UITestResult 来更新图形化的测试状态。 TestResult 是框架的一个扩展点(extension point)。客户能够自定义它们的 TestResult 类,例如 HTMLTestResult 可将结果上报为一个 HTML 文档