假设你有20,或者你甚至有100,与Calc类做相反测试,所有这些看起来令人吃惊的相似。现在一个计划的改变迫使你不得不删除默认的Calc构造器并且使用一个含有一些参数的不同的构造器。马上,你所有的测试就被暂停了。你可能可以很轻易的发现问题的关键并修复它,但你也可能做不到。最主要的问题是你将会浪费很多宝贵时间在修理你的测试上面。如果你在你的测试类之中使用一个整体的方法去创建Calc 实例,就像Figure 2所显示的那样,这些就并不是个问题。
我已经对测试做了一些改变已使它们能够具有更多可维护性。首先,我将新创建的代码迁移至可以再度使用的整体方法之中。这就意味着我只需仅仅改变一个简单的方法以使得在这个测试类中的所有测试在一个新的构造器中的能够正常的工作。另外一个为创造问题而设的简单解决方法是把创作物迁移到测试类的方法之中。不幸运的是,这个能够很好的工作仅仅在你重新使用一个对象并在一些测试中把它当作一个局部类变量。如果你仅仅为一些测试使用它(部分相关成员),你倒不如在测试中将它们实例化,并且使它们更具易读性。
顺便一提的是,请注意,我已经将方法命名为Factory_CreateDefaultCalc 。我很喜欢将我测试中的任何帮助方法用特殊的前缀来命名,这样我就能很轻易的掌握它是做什么用的。这样对易读性也是非常有帮助的。
我的第二个改变是重新使用测试中的声明代码,并将这段代码迁移到一个确认方法之中。所谓确认方法是你测试中的一个可再度使用的方法, 这个方法包含了一个声明语句但是它可以接受不同输入和在输入的基础上进行校验。当你在不同输入或者不同的初始状态下一次又一次的声明同一事物时,你可以使用确认方法。这一方法的优点是既使在一个不同的方法里面声明,如果这个声明失败了你将可以继续保有一个异常处理,而且原始调用测试将会显示在测试失败输出窗口之中。
我也在Calc 中传递实例而不是使用一个局部变量,因此我知道我经常传递一个实例,而且这个实例是调用测试将其初始化的。当你想要改变对象状态时你可能想要做同样的事情,举个例子来说,当在测试下或者在将会传递给测试的对象下配置特殊对象时,可以使用特殊的Configure_XX方法。这些方法应该能够解释他们配置一个对象将会用来做什么用。Figure 3之中的代码就是以上方法的实例。
这个测试拥有很多设置代码可以用来处理向注册管理器对象中添加初始状态,它是这个测试类之中的成员。在此的确也有一些重复。Figure 4显示了在初始代码之外这些事例在因式分解之后将会如何变化。
修订测试具有非常高的可读性和稳定性。仅仅需要注意的是不要那么的refactor你的测试,他们可能会以一个单一的,不可读的代码行作为结束。应该注意的是我在这里可能依然使用一个Verify_XX 方法,但是这并不是我真正要在这里加以说明的。
消除测试之间的依赖关系
一个测试应该能够自我独立。它不应该与其他测试相关联,也不应该依赖任何具有特殊运行顺序的测试,它应该能够获得你所写的所有测试,可以随意运行所有测试或者只运行其中的一部分,并且是以任何顺序,而且要能够确保它们无论怎样都应该正确的运行。如果你不能够执行这个规则,你将会只在某种特殊的情况下按照预期的表现来运行的状况下结束你的测试。这样子的话,当你在最终期限下与此同时你还想确定你没有向系统之中引进新的问题的时候,当然就会出现问题。你可能很困惑而且考虑着是不是你的代码出现问题,这时,在事实上,问题其实仅仅是你的测试运行顺序所引起的。因此,你可能开始错过了一些在测试中失败的结果而且使它越写越少。这将会是个长期的过程。
如果你从一个测试调出至另一个测试之中,你应该在它们之间创建一个从属关系。你本质上说是在一个测试中测试两个事物(我将会在下一章中解释为什么这会成为一个问题)。就另一方面来说,如果你有测试B,它与测试A 所产生的状态是不相关的,那么你会陷入“顺序”陷阱之中。如果你或者其他人想要改变测试A,测试B将会暂停而且你不知它暂停的原因。对这些故障进行故障处理会浪费很多时间。
使用 和方法是本质上能够获得更好的测试隔离。确定你的测试数据时刻是最新的,而且测试下对象的也具有新的实例,而且所有的状态可以提前预知,而且无论你的测试在任何地方或者任何时间被运行,运行的情况都是相同的。
原文转自:http://www.uml.org.cn/Test/200605091.htm