您的位置:软件测试 > 开源软件测试 > 开源单元测试工具 >
使用模仿对象Mock object进行单元测试
作者:网络转载 发布时间:[ 2013/3/11 17:02:30 ] 推荐标签:

后退到测试类内部,我们将 MockTransaction 类定义为成员类,如清单 5 中所示:

清单 5. 将 MockTransaction 定义为成员类:

private MockTransaction extends Transaction {
    private boolean processCalled = false;
    // override process method so that no real work is done
    public void process() {
      processCalled = true;
      setStatus(Status.SUCCESS);
    }
    public void validate() {
      assertTrue(processCalled);
    }
  }
 


后,我们可以重写测试,以便被测试的对象使用 MockTransaction 类,而不是使用实际类,如清单 6 中所示:

清单 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 等人在设计模式中详细描述的那样。(请参阅参考资料)。实际上,许多人已经用工厂对象来着手这种方法,而不是用工厂方法 — 我们以前是这样做的,但很快放弃了。

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

它增加了复杂性,而没有相应地增加功能。

它会迫使您更改目标对象的公用接口。如果必须传入抽象工厂对象,那么您必须添加一个新的公用构造函数或赋值(mutator)方法。

许多语言对于“工厂”这一概念都附有一些约定,它们会使您误入歧途。例如,在 Java 语言中,工厂通常作为静态方法实现;在这种情况下,这是不合适的。

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

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

参考资料

由 Tim Mackinnon、Steve Freeman 和 Philip Craig 合著的文章“Endo-Testing: Unit Testing with Mock Objects”介绍了术语模仿对象。

Mock Objects Project 是支持模仿对象实现的框架。

工厂方法和抽象工厂设计模式的来源是由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides(也称为四人组(Gang of Four))合著的 Design Patterns: Elements of Reusable Object-Oriented Software(Addison-Wesley,1997 年)。

如果您不熟悉设计模式,那么您还需要学习这个由两部分组成的免费教程:

Java design patterns 101(developerWorks,2002 年 1 月)介绍了模式。

Java design patterns 201(developerWorks,2002 年 4 月)介绍了四人组未描述到的其它模式。

请阅读在线文章 The Factory Method Design Pattern。

由 Martin Fowler 维护的 Refactoring Home Page 是程序员的主要参考资料。

另外,由 Martin Fowler 编写的 Refactoring: Improving the Design of Existing Code(Addison-Wesley,1999 年)值得一读。

JUnit 是流行的 Java 语言的单元测试框架。

请参考 Purple Technology 的 XP 和重构参考资料列表。

由 XP 教练和 Java 开发人员 Roy Miller 合著的专栏文章 Demystifying Extreme Programming 洞察了这个方法,其中,测试是关键组件。请务必访问一下附随的论坛。

Nicholas Lesiecki 的“Test Flexibly with AspectJ and mock objects”(developerWorks,2002 年 5 月)详细地描述了在单元测试时如何使用 AspectJ 和模仿对象。

由 Eric Allen 著的“Diagnosing Java code: Unit tests and automated code analysis working together”(developerWorks,2002 年 10 月)研究了单元测试和静态分析之间的关系。

WebSphere 开发者园地中“Application Quality Assurance: Unit Testing”一文研究了使用 JUnit 的单元测试。

WebSphere 开发者园地中“Debugging and Unit-Testing Server-Side Web Applications”一文也描述了包括交互式调试和迭代单元测试的服务器端 Web 开发的方案。

在 developerWorks Java 技术专区可找到数百篇有关 Java 技术的文章和教程。

关于作者

Alexander Day Chaffee 是 Purple Technology 的创办人,该公司提供了 Java 语言、极端编程和开放源码咨询和培训。他管理 jGuru 的 Servlets FAQ。作为 EarthWeb 的软件工程主管,Alex 与人共同创建了 Gamelan,这是 Java 社区的正式目录。可以通过 alex@jguru.com 与他联系。

William Pietri 的父亲是一位系统分析师和企业家,William 在十三岁时开始利用计算机赚取午餐费。从那以后,他几乎从事过技术领域的各个方面,从技术支持到系统管理到软件工程到用户界面设计。他是 Scissor(技术咨询公司)的创办人。可以通过 william@scissor.com 与 William 联系。

上一页123下一页
软件测试工具 | 联系我们 | 投诉建议 | 诚聘英才 | 申请使用列表 | 网站地图
沪ICP备07036474 2003-2017 版权所有 上海泽众软件科技有限公司 Shanghai ZeZhong Software Co.,Ltd