MockTransaction mockTransaction; |
该解决方案产生了一个稍长的测试,但该测试只关注正在测试的类的直接行为,
而不是 ATM 接口之外整个系统的行为。也就是说,我们不再检查测试帐户的最终余额是否正确;
我们将在对
Transaction
对象的单元测试中检查该函数,而不是在对
AtmGui
对象的单元测试中。
注:根据模仿对象的创造者所说,它应该在其
validate()
方法内部执行自己的所有验证。
在本示例中,为了清晰起见,我们将验证的某些部分放在了测试方法内部。
随着您更加熟练地使用模仿对象,对于将多少验证职责代理给模仿对象,您将会深有体会。
|
在清单 6 中,我们使用了
AtmGui
的匿名内部子类来覆盖
createTransaction
方法。
因为我们只需要覆盖一个简单的方法,所以这是实现我们目标的简明方法。
如果我们覆盖多个方法或在许多测试之间共享
AtmGui
子类,那么创建一个完整的(非匿名)成员类是值得的。
我们还使用了实例变量来存储对模仿对象的引用。这是在测试方法和特殊化类之间共享数据的最简单方法。这是可以接受的,因为我们的测试框架不是多线程的或可重入的。(如果它是多线程的或可重入的,则必须用
synchronized
块保护我们自己。)
最后,我们将模仿对象本身定义为测试类的专用内部类 ― 这通常是一种便利的方法, 因为将模仿对象就放在使用它的测试代码旁边会更加清楚,又因为内部类有权访问包含它们的类的实例变量。
|
因为我们覆盖了工厂方法来编写这个测试,所以其结果是:我们的测试不再包括任何 原始创建代码(现在它在基类的工厂方法内部)。 添加确实包括该代码的测试也许是有益的。这与调用基类的工厂方法并断言返回对象具有正确类型一样简单。例如:
AtmGui atm = new AtmGui(); |
注:相反,
assertTrue(t instanceof Transaction)
不能满足,因为
MockTransaction
也是
Transaction
。
|
此时,您可能很想更进一步并用成熟的抽象工厂对象替换工厂方法, 如 Erich Gamma 等人在 设计模式中详细描述的那样。(请参阅 参考资料)。 实际上,许多人已经用工厂对象来着手这种方法,而不是用工厂方法 ― 我们以前是这样做的,但很快就放弃了。
将第三种对象类型(角色)引入系统会有一些潜在的缺点:
- 它增加了复杂性,而没有相应地增加功能。
- 它会迫使您更改目标对象的公用接口。如果必须传入抽象工厂对象,那么您必须添加一个新的公用构造函数或赋值(mutator)方法。
- 许多语言对于“工厂”这一概念都附有一些约定,它们会使您误入歧途。例如,在 Java 语言中,工厂通常作为静态方法实现;在这种情况下,这是不合适的。
请记住,本练习的宗旨是使对象更易于 测试。通常, 用于可测性的设计可以将对象的 API 推向一种更清晰更模块化的状态。但它会走得太远。测试驱动的设计更改不应该污染原始对象的公用接口。
在 ATM 示例中,对于产品代码,
AtmGui
对象始终只产生一种类型的
Transaction
对象(实际类型)。测试代码希望它产生另一种类型的对象(模仿对象)。但强迫公用 API 适应工厂对象或抽象工厂(只因为测试代码要求它这样)是错误的设计。
如果产品代码无需实例化该合作者的多个类型,那么添加该功能将使最终的设计不必要地变得难于理解。
文章来源于领测软件测试网 https://www.ltesting.net/