异常,jυUnit测试框架将这种错误归入 Failes并加以记录,同时标 志为未通过测试。如果该类方法中指定一个 String类型的传参则 该参数将被做为 Assertion Failed error异常的标识信息,告诉测试 人员改异常的详细信息。 JUnit提供了6大类31组断言方法,包括基础断言、数字断言、 字符断言、布尔断言、对象断言。 其中 assertEquals( Object expected, Object actua内部逻辑判断使 用 equals()方法,这表明断言两个实例的内部哈希值是否相等时 最好使用该方法对相应类实例的值进行比较。而 assertsame ( Object expected, Object actual)内部逻辑判断使用了Java运算 符“==”,这表明该断言判断两个实例是否来自于同一个引用 ( Reference),最好使用该方法对不同类的实例的值进行比对。 asser equals( String message, String expected, String actual)该方法对 两个字符串进行逻辑比对,如果不匹配则显示着两个字符串有差 异的地方。 Comparison Failure类提供两个字符串的比对,不匹配 则给出详细的差异字符。 TestSuite测试包类—多个测试的组合 Test Suite类负责组装多个 Test Cases。待测得类中可能包括了对 被测类的多个测试,而 Testsuit负责收集这些测试,使我们可以 在一个测试中,完成全部的对被测类的多个测试。 Testsuite类实现了Test接口,且可以包含其它的 Test suites。它 可以处理加入Test时的所有抛出的异常。 TestSuite处理测试用例有6个规约(否则会被拒绝执行测试) A测试用例必须是公有类( Public) B测试用例必须继承与 Test case类 C测试用例的测试方法必须是公有的( Public) D测试用例的测试方法必须被声明为void E测试用例中测试方法的前置名词必须是test F测试用例中测试方法误任何传递参数 n Testresult结果类和其它类与接口 TestResult结果类集合了任意测试累加结果,通过 TestResult实例 传递个每个测试的Run()方法。 TestResult在执行 Test Case是 如果失败会异常抛出 Test listener接口是个事件监听规约,可供 TestRunner类使用。 它通知 listener的对象相关事件,方法包括测试开始 startLes(Test test,测试结束 endTest( Testtest,错误,增加异常 add error(Test test. throwable t)和增加失败 add Failure(Test test, Assertion Failed Error t) Test Failure失败类是个“失败”状况的收集类,解释每次测试执 行过程中出现的异常情况。其 toString方法返回“失败”状况的 简要描述
异常,JUnit 测试框架将这种错误归入 Failes 并加以记录,同时标 志为未通过测试。如果该类方法中指定一个 String 类型的传参则 该参数将被做为 AssertionFailedError 异常的标识信息,告诉测试 人员改异常的详细信息。 JUnit 提供了 6 大类 31 组断言方法,包括基础断言、数字断言、 字符断言、布尔断言、对象断言。 其中 assertEquals(Object expcted,Object actual)内部逻辑判断使 用 equals()方法,这表明断言两个实例的内部哈希值是否相等时, 最好使用该方法对相应类实例的值进行比较。而 assertSame (Object expected,Object actual)内部逻辑判断使用了 Java 运算 符“==”,这表明该断言判断两个实例是否来自于同一个引用 (Reference),最好使用该方法对不同类的实例的值进行比对。 asserEquals(String message,String expected,String actual)该方法对 两个字符串进行逻辑比对,如果不匹配则显示着两个字符串有差 异的地方。ComparisonFailure 类提供两个字符串的比对,不匹配 则给出详细的差异字符。 TestSuite 测试包类——多个测试的组合 TestSuite 类负责组装多个 Test Cases。待测得类中可能包括了对 被测类的多个测试,而 TestSuit 负责收集这些测试,使我们可以 在一个测试中,完成全部的对被测类的多个测试。 TestSuite 类实现了 Test 接口,且可以包含其它的 TestSuites。它 可以处理加入 Test 时的所有抛出的异常。 TestSuite 处理测试用例有 6 个规约(否则会被拒绝执行测试) A 测试用例必须是公有类(Public) B 测试用例必须继承与 TestCase 类 C 测试用例的测试方法必须是公有的( Public ) D 测试用例的测试方法必须被声明为 Void E 测试用例中测试方法的前置名词必须是 test F 测试用例中测试方法误任何传递参数 n TestResult 结果类和其它类与接口 TestResult 结果类集合了任意测试累加结果,通过 TestResult 实例 传递个每个测试的 Run()方法。TestResult 在执行 TestCase 是 如果失败会异常抛出 TestListener 接口是个事件监听规约,可供 TestRunner 类使用。 它通知 listener 的对象相关事件,方法包括测试开始 startTest(Test test),测试结束 endTest(Test test),错误,增加异常 addError(Test test,Throwable t)和增加失败 addFailure(Test test,AssertionFailedError t) TestFailure 失败类是个“失败”状况的收集类,解释每次测试执 行过程中出现的异常情况。其 toString()方法返回“失败”状况的 简要描述
122 JUnit架构 JUnit的架构可以参照下图: Client Adapte ternata Servicel Internal Microkernel Servicel 13 JUnit设计模式 131简介 在本文中我们将匆匆一瞥其内中细节,并向你展示该框架本身是如何被构造的 我们细致地研究 JUint框架并思索如何来构造它。我们发现了许多不同层次上的教训 在本文中,我们将尝试着立刻与它们进行沟通,这是一个令人绝望的任务,但至少它 是在我们向你展示设计和构造一件价值被证实的软件的上下文中来进行的 我们引发了一个关于框架目标的讨论。在对框架本身的表达期间,目标将重复出现许 多小的细节中。此后,我们提出框架的设计和实现。设计将从模式(惊奇,惊奇)的 角度进行描述,并作为优美的程序来予以实现。我们总结了一些优秀的关于框架开发 的想法
1.2.2 JUnit 架构 JUnit 的架构可以参照下图: 1.3 JUnit 设计模式 1.3.1 简介 在本文中我们将匆匆一瞥其内中细节,并向你展示该框架本身是如何被构造的。 我们细致地研究 JUint 框架并思索如何来构造它。我们发现了许多不同层次上的教训。 在本文中,我们将尝试着立刻与它们进行沟通,这是一个令人绝望的任务,但至少它 是在我们向你展示设计和构造一件价值被证实的软件的上下文中来进行的。 我们引发了一个关于框架目标的讨论。在对框架本身的表达期间,目标将重复出现许 多小的细节中。此后,我们提出框架的设计和实现。设计将从模式(惊奇,惊奇)的 角度进行描述,并作为优美的程序来予以实现。我们总结了一些优秀的关于框架开发 的想法
132什么是 JUnit的目标呢? 首先,我们不得不回到开发的假定上去。如果缺少一个程序特性的自动测试 ( automated test),我们便假定其无法工作。这看起来要比主流的假定更加安全,主 流的假定认为如果开发者向我们保证一个程序特性能够工作,那么现在和将来其都会 永远工作。 从这个观点来看,当开发者编写和调试代码时,它们的工作并没有完成,它们还要必 须编写测试来演示程序能够工作。然而,每个人都太忙,他们要做的事情太多,他们 没有充足的时间用于测试。我已经有太多的代码需要编写,要我如何再来编写测试代 码?回答我,强硬的项目经理先生。因此,首要目标就是编写一个框架,在这个框架 中开发者能够看到实际来编写测试的希望之光。该框架必须要使用常见的工具,从而 学习起来不会有太多的新东西。其不能比完全编写一个新测试所必须的工作更多。必 须排除重复性的工作。 如果所有测试都这样去做的话,你将可以仅在一个调试器中编写表达式来完成。然而, 这对于测试而言尚不充分。告诉我你的程序现在能够工作,对我而言并没有什么帮助 因为它并没有向我保证你的程序从我现在集成之后的每一分钟都将会工作,以及它并 没有向我保证你的程序将依然能够工作五年,那时你已经离开了很长的时间 于是,测试的第二个目标就是生成可持续保持其价值的测试。除原作者以外的其他人 必须能够执行测试并解释其结果。应该能够将不同作者的测试结合起来并在一起运 行,而不必担心相互冲突。 最后,必须能够以现有的测试作为支点来生成新的测试。生成一个装置( setup)或 夹具( fixture)是昂贵的,并且一个框架必须能够对夹具进行重用,以运行不同的测 试。哦,还有别的吗? 133 JUnit的设计 JUnit的设计将以一种首次在 Patterns Generate Architectures(请参见" Patterns Generate Architectures", Kent Beck and Ralph Johnson, ECOOP94)中使用的风格来呈 现。其思想是通过从零开始来应用模式,然后一个接一个,直至你获得系统架构的方 式来讲解一个系统的设计。我们将提出需要解决的架构问题,总结用来解决问题的模 式,然后展示如何将模式应用于 JUnit。 1331由此开始一 TestCase 首先我们必须构建一个对象来表达我们的基本概念, Testcase(测试案例)。开发者经 常在头脑中存在着测试案例,但在实现它们的时候却采用了许多不同的方式一 1.打印语句 2.调试器表达式 3测试脚本
1.3.2 什么是 JUnit 的目标呢? 首先,我们不得不回到开发的假定上去。如果缺少一个程序特性的自动测试 (automated test),我们便假定其无法工作。这看起来要比主流的假定更加安全,主 流的假定认为如果开发者向我们保证一个程序特性能够工作,那么现在和将来其都会 永远工作。 从这个观点来看,当开发者编写和调试代码时,它们的工作并没有完成,它们还要必 须编写测试来演示程序能够工作。然而,每个人都太忙,他们要做的事情太多,他们 没有充足的时间用于测试。我已经有太多的代码需要编写,要我如何再来编写测试代 码?回答我,强硬的项目经理先生。因此,首要目标就是编写一个框架,在这个框架 中开发者能够看到实际来编写测试的希望之光。该框架必须要使用常见的工具,从而 学习起来不会有太多的新东西。其不能比完全编写一个新测试所必须的工作更多。必 须排除重复性的工作。 如果所有测试都这样去做的话,你将可以仅在一个调试器中编写表达式来完成。然而, 这对于测试而言尚不充分。告诉我你的程序现在能够工作,对我而言并没有什么帮助, 因为它并没有向我保证你的程序从我现在集成之后的每一分钟都将会工作,以及它并 没有向我保证你的程序将依然能够工作五年,那时你已经离开了很长的时间。 于是,测试的第二个目标就是生成可持续保持其价值的测试。除原作者以外的其他人 必须能够执行测试并解释其结果。应该能够将不同作者的测试结合起来并在一起运 行,而不必担心相互冲突。 最后,必须能够以现有的测试作为支点来生成新的测试。生成一个装置(setup)或 夹具(fixture)是昂贵的,并且一个框架必须能够对夹具进行重用,以运行不同的测 试。哦,还有别的吗? 1.3.3 JUnit 的设计 JUnit 的设计将以一种首次在 Patterns Generate Architectures(请参见"Patterns Generate Architectures", Kent Beck and Ralph Johnson, ECOOP 94)中使用的风格来呈 现。其思想是通过从零开始来应用模式,然后一个接一个,直至你获得系统架构的方 式来讲解一个系统的设计。我们将提出需要解决的架构问题,总结用来解决问题的模 式,然后展示如何将模式应用于 JUnit。 1.3.3.1 由此开始-TestCase 首先我们必须构建一个对象来表达我们的基本概念,TestCase(测试案例)。开发者经 常在头脑中存在着测试案例,但在实现它们的时候却采用了许多不同的方式- 1. 打印语句 2. 调试器表达式 3. 测试脚本
如果我们想要轻松地操纵测试,就必须将它们构建成对象。这将会获取到一个仅仅是 隐藏在开发者头脑中的测试,并使之具体化,其支持我们创建测试的目标,即能够持 续地保持它们的价值。同时,对象的开发者比较习惯于使用对象来进行开发,因此将 测试构建成对象的决定支持我们的目标一使测试的编写更加吸引人(或至少是不太华 丽 Command(命令)模式(请参见 Gamma,E,etal. Design Patterns: Elements of reusable Object-Oriented Software, Addison- Wesley, Reading,MA,1995)则能够比较好地满足我 们的需求。摘引其意图( intent),“将一个请求封装成一个对象,从而使你可用不同 的请求对客户进行参数化;对请求进行排队或记录请求日志…” Command告诉我们 可以为一个操作生成一个对象并给出它的一个“ execute(执行)”方法。以下代码定 义了 TestCase类: public abstract class TestCase implements Test 因为我们期望可以通过继承来对该类进行重用,我们将其声明为“ public abstract 暂时忽略其实现接口Test的事实。鉴于当前设计的需要,你可以将 Test case看作是 个孤立的类。 每一个 Testcase在创建时都要有一个名称,因此若一个测试失败了,你便可识别出失 败的是哪个测试。 public abstract class TestCase implements Test i private final String fName public TestCase(String name) public abstract void run( 为了阐述JUnt的演变过程,我们将使用图( diagram)来展示构架的快照( snapshot) 我们使用的标记很简单。其使用包含相关模式的尖方框来标注类。当类在模式中的角 色(role)显而易见时,则仅显示模式的名称。如果角色并不清晰,则在尖方框中增 加与该类相关的参与者的名称。该标记可使图的混乱度降到最小限度,并首次见诸于 Applying Design Patterns in Java (H L Gamma, E, Applying Design Patterns in Java, in Java gems, SIGS Reference Library,1997)。图1展示了这种应用于 Testcase中的标记。 由于我们是在处理一个单独的类并且没有不明确的地方,因此仅显示模式的名称。 Testcase Command runo
如果我们想要轻松地操纵测试,就必须将它们构建成对象。这将会获取到一个仅仅是 隐藏在开发者头脑中的测试,并使之具体化,其支持我们创建测试的目标,即能够持 续地保持它们的价值。同时,对象的开发者比较习惯于使用对象来进行开发,因此将 测试构建成对象的决定支持我们的目标-使测试的编写更加吸引人(或至少是不太华 丽)。 Command(命令)模式(请参见 Gamma, E., et al. Design Patterns: Elements of Reusable Object‐Oriented Software, Addison‐Wesley, Reading, MA, 1995)则能够比较好地满足我 们的需求。摘引其意图(intent),“将一个请求封装成一个对象,从而使你可用不同 的请求对客户进行参数化;对请求进行排队或记录请求日志...”Command 告诉我们 可以为一个操作生成一个对象并给出它的一个“execute(执行)”方法。以下代码定 义了 TestCase 类: public abstract class TestCase implements Test { … } 因为我们期望可以通过继承来对该类进行重用,我们将其声明为“public abstract”。 暂时忽略其实现接口 Test 的事实。鉴于当前设计的需要,你可以将 TestCase 看作是 一个孤立的类。 每一个 TestCase 在创建时都要有一个名称,因此若一个测试失败了,你便可识别出失 败的是哪个测试。 public abstract class TestCase implements Test { private final String fName; public TestCase(String name) { fName= name; } public abstract void run(); … } 为了阐述 JUnit 的演变过程,我们将使用图(diagram)来展示构架的快照(snapshot)。 我们使用的标记很简单。其使用包含相关模式的尖方框来标注类。当类在模式中的角 色(role)显而易见时,则仅显示模式的名称。如果角色并不清晰,则在尖方框中增 加与该类相关的参与者的名称。该标记可使图的混乱度降到最小限度,并首次见诸于 Applying Design Patterns in Java(请参见 Gamma, E., Applying Design Patterns in Java, in Java Gems, SIGS Reference Library, 1997)。图 1 展示了这种应用于 TestCase 中的标记。 由于我们是在处理一个单独的类并且没有不明确的地方,因此仅显示模式的名称
图1 Test case应用 Command 1.3.32空白填充-run0 接下来要解决的问题是给开发者一个便捷的“地方”,用于放置他们的夹具代码和测 试代码。将 TestCase声明为 abstract是指开发者希望通过子类化( subclassing)来对 Test case进行重用。然而,如果我们所有能作的就是提供一个只有一个变量且没有行 为的超类,那么将无法做太多的工作来满足我们的首个目标一使测试更易于编写。 幸运的是,所有测试都具有一个共同的结构一建立一个测试夹具,在夹具上运行一些 代码,检査结果,然后清理夹具。这意味着每一个测试将与一个新的夹具一起运行, 并且一个测试的结果不会影响到其它测试的结果。这支持测试价值最大化的目标。 Template Method(模板方法)比较好地涉及到我们的问题。摘引其意图,“定义一个 操作中算法的骨架,并将一些步骤延迟到子类中。 Template Method使得子类能够不 改变一个算法的结构便可重新定义该算法的某些特定步骤。”这完全恰当。我们就是 想让开发者能够分别来考虑如何编写夹具(建立和拆卸)代码,以及如何编写测试代 码。不管怎样,这种执行的次序对于所有测试都将保持相同,而不管夹具代码如何编 写,或测试代码如何编写。 Template Method如下 ublic void run( t setUp t(); tearDown(; 这些方法被缺省实现为“什么都不做” protected void run Test(i protected void setup protected void tear Down( 由于 setUp和 tear Down会被用来重写( override),而且其将由框架来进行调用,因 此我们将其声明为 protected。我们的第二个快照如图2所示。 Testcase run Temp late Method setUp 图2 TestCase. run(应用 Template Method
图 1 TestCase 应用 Command 1.3.3.2 空白填充-run() 接下来要解决的问题是给开发者一个便捷的“地方”,用于放置他们的夹具代码和测 试代码。将 TestCase 声明为 abstract 是指开发者希望通过子类化(subclassing)来对 TestCase 进行重用。然而,如果我们所有能作的就是提供一个只有一个变量且没有行 为的超类,那么将无法做太多的工作来满足我们的首个目标-使测试更易于编写。 幸运的是,所有测试都具有一个共同的结构-建立一个测试夹具,在夹具上运行一些 代码,检查结果,然后清理夹具。这意味着每一个测试将与一个新的夹具一起运行, 并且一个测试的结果不会影响到其它测试的结果。这支持测试价值最大化的目标。 Template Method(模板方法)比较好地涉及到我们的问题。摘引其意图,“定义一个 操作中算法的骨架,并将一些步骤延迟到子类中。Template Method 使得子类能够不 改变一个算法的结构便可重新定义该算法的某些特定步骤。”这完全恰当。我们就是 想让开发者能够分别来考虑如何编写夹具(建立和拆卸)代码,以及如何编写测试代 码。不管怎样,这种执行的次序对于所有测试都将保持相同,而不管夹具代码如何编 写,或测试代码如何编写。 Template Method 如下: public void run() { setUp(); runTest(); tearDown(); } 这些方法被缺省实现为“什么都不做”: protected void runTest() { } protected void setUp() { } protected void tearDown() { } 由于 setUp 和 tearDown 会被用来重写(override),而且其将由框架来进行调用,因 此我们将其声明为 protected。我们的第二个快照如图 2 所示。 图 2 TestCase.run()应用 Template Method