• 软件测试技术
  • 软件测试博客
  • 软件测试视频
  • 开源软件测试技术
  • 软件测试论坛
  • 软件测试沙龙
  • 软件测试资料下载
  • 软件测试杂志
  • 软件测试人才招聘
    暂时没有公告

字号: | 推荐给好友 上一篇 | 下一篇

用模仿对象替换合作者以改进单元测试

发布: 2008-7-30 09:55 | 作者: Alexander Day Chaffe | 来源: IBM | 查看: 71次 | 进入软件测试论坛讨论

领测软件测试网

清单 6. 使用 MockTransaction 类
 MockTransaction mockTransaction;
public void testCheckingWithdrawal() {
mockTransaction = new MockTransaction();
AtmGui atm = new AtmGui() {
protected Transaction createTransaction() {
return mockTransaction;
}
};
insertCardAndInputPin(atm);
atm.pressButton("Withdraw");
atm.pressButton("Checking");
atm.pressButtons("1", "0", "0", "0", "0");
assertContains("$100.00", atm.getDisplayContents());
atm.pressButton("Continue");
assertEquals(100.00, mockTransaction.getAmount());
assertEquals(TEST_CHECKING_ACCOUNT,
mockTransaction.getSourceAccount());
assertEquals(TEST_CASH_ACCOUNT,
mockTransaction.getDestAccount());
mockTransaction.validate();
}

该解决方案产生了一个稍长的测试,但该测试只关注正在测试的类的直接行为, 而不是 ATM 接口之外整个系统的行为。也就是说,我们不再检查测试帐户的最终余额是否正确; 我们将在对 Transaction 对象的单元测试中检查该函数,而不是在对 AtmGui 对象的单元测试中。

注:根据模仿对象的创造者所说,它应该在其 validate() 方法内部执行自己的所有验证。 在本示例中,为了清晰起见,我们将验证的某些部分放在了测试方法内部。 随着您更加熟练地使用模仿对象,对于将多少验证职责代理给模仿对象,您将会深有体会。





回页首


内部类魔法

在清单 6 中,我们使用了 AtmGui 的匿名内部子类来覆盖 createTransaction 方法。 因为我们只需要覆盖一个简单的方法,所以这是实现我们目标的简明方法。 如果我们覆盖多个方法或在许多测试之间共享 AtmGui 子类,那么创建一个完整的(非匿名)成员类是值得的。

我们还使用了实例变量来存储对模仿对象的引用。这是在测试方法和特殊化类之间共享数据的最简单方法。这是可以接受的,因为我们的测试框架不是多线程的或可重入的。(如果它是多线程的或可重入的,则必须用 synchronized 块保护我们自己。)

最后,我们将模仿对象本身定义为测试类的专用内部类 ― 这通常是一种便利的方法, 因为将模仿对象就放在使用它的测试代码旁边会更加清楚,又因为内部类有权访问包含它们的类的实例变量。





回页首


小心不出大错

因为我们覆盖了工厂方法来编写这个测试,所以其结果是:我们的测试不再包括任何 原始创建代码(现在它在基类的工厂方法内部)。 添加确实包括该代码的测试也许是有益的。这与调用基类的工厂方法并断言返回对象具有正确类型一样简单。例如:

   AtmGui atm = new AtmGui();
Transaction t = atm.createTransaction();
assertTrue(!(t instanceof MockTransaction));

注:相反, assertTrue(t instanceof Transaction) 不能满足,因为 MockTransaction 也是 Transaction





回页首


从工厂方法到抽象工厂

此时,您可能很想更进一步并用成熟的抽象工厂对象替换工厂方法, 如 Erich Gamma 等人在 设计模式中详细描述的那样。(请参阅 参考资料)。 实际上,许多人已经用工厂对象来着手这种方法,而不是用工厂方法 ― 我们以前是这样做的,但很快就放弃了。

将第三种对象类型(角色)引入系统会有一些潜在的缺点:

  1. 它增加了复杂性,而没有相应地增加功能。
  2. 它会迫使您更改目标对象的公用接口。如果必须传入抽象工厂对象,那么您必须添加一个新的公用构造函数或赋值(mutator)方法。
  3. 许多语言对于“工厂”这一概念都附有一些约定,它们会使您误入歧途。例如,在 Java 语言中,工厂通常作为静态方法实现;在这种情况下,这是不合适的。

请记住,本练习的宗旨是使对象更易于 测试。通常, 用于可测性的设计可以将对象的 API 推向一种更清晰更模块化的状态。但它会走得太远。测试驱动的设计更改不应该污染原始对象的公用接口。

在 ATM 示例中,对于产品代码, AtmGui 对象始终只产生一种类型的 Transaction 对象(实际类型)。测试代码希望它产生另一种类型的对象(模仿对象)。但强迫公用 API 适应工厂对象或抽象工厂(只因为测试代码要求它这样)是错误的设计。 如果产品代码无需实例化该合作者的多个类型,那么添加该功能将使最终的设计不必要地变得难于理解。

延伸阅读

文章来源于领测软件测试网 https://www.ltesting.net/

33/3<123

关于领测软件测试网 | 领测软件测试网合作伙伴 | 广告服务 | 投稿指南 | 联系我们 | 网站地图 | 友情链接
版权所有(C) 2003-2010 TestAge(领测软件测试网)|领测国际科技(北京)有限公司|软件测试工程师培训网 All Rights Reserved
北京市海淀区中关村南大街9号北京理工科技大厦1402室 京ICP备2023014753号-2
技术支持和业务联系:info@testage.com.cn 电话:010-51297073

软件测试 | 领测国际ISTQBISTQB官网TMMiTMMi认证国际软件测试工程师认证领测软件测试网