Nicholas Lesiecki (ndlesiecki@apache.org) 主要软件工程师,eBlox, Inc.
在开发过程中结合了单元测试的程序员都了解这样做带来的好处:代码更简洁,敢于重构、速度更快。但即便是最执著的单元测试者,在碰到测试行为依靠于系统状态的类的情况时,也会显得信心不足。Nicholas Lesiecki 是一名受人尊敬的 java 程序员,也是 XP 社区的领导者,他将介绍围绕测试案例隔离的问题,并向我们展示如何使用模拟对象(mock object)和 aspectJ 来开发精确和健壮的单元测试。请通过单击文章顶部或底部的讨论,在讨论论坛与作者和其它读者分享您对本文的看法。 最近,人们对极端编程(Extreme PRogramming,XP)的关注已经扩大到它的一个最具可移植性的应用上:单元测试和最初测试设计。因为软件工作室已经开始采用 XP 的开发方法,我们可以看到,因为有了一套全面的单元测试工具,很多开发者的开发质量和速度都得到了提高。但编写好的单元测试耗时费力。因为每个单元都与其它单元合作,所以编写单元测试可能需要大量的设置代码。这使得测试变得更加昂贵,而且在特定情况下(比如代码充当远程系统的客户机时),这样的测试可能几乎无法实现。
在 XP 中,单元测试弥补了集成测试和验收测试的不足。后两种测试类型可能由独立的小组进行,或者作为独立的活动进行。但是单元测试是与要测试的代码同时编写的。面对日益逼近的截止期限和令人头痛的单元测试带来的压力,我们很可能随便编写一个测试了事,或者完全放弃测试。因为 XP 依靠于积极的动机和自给自足的习惯,所以 XP 过程(和项目!)的最佳利益就是使测试保持集中和易于编写。
清单 1. CustomerManager 的远程接口 public interface CustomerManager extends EJBObject {
/** * Returns a String[] representing the names of customers in the system * over a certain age. */ public String[] getCustomersOver(int ageInYears) throws RemoteException;
/** * Registers a new customer with the system. If the customer already * exists within the system, this method throws a NameExistsException. */ public void register(String name) throws RemoteException, NameExistsException; }
清单 2. EJB 客户机代码 public class ClientBean { private Context initialContext; private CustomerManager manager;
/** * Includes standard code for referencing an EJB. */ public ClientBean() throws Exception{ initialContext = new InitialContext(); Object obj = initialContext.lookup("java:comp/env/ejb/CustomerManager"); CustomerManagerHome managerHome = (CustomerManagerHome)obj;
/*Resin uses Burlap instead of RMI-IIOP as its default * network protocol so the usual RMI cast is omitted. * Mock Objects survive the cast just fine. */ manager = managerHome.create(); }
public String[] getCustomers(int ageInYears) throws Exception{ return manager.getCustomersOver(ageInYears); }
public static final String NEW_CUSTOMER = "Bob Smith"; public static final String EXISTING_CUSTOMER = "Philomela Deville"; public static final int MAGIC_AGE = 35;
public void testGetCustomers() throws Exception { ClientBean client = new ClientBean(); String[] results = client.getCustomers(MAGIC_AGE); assertEquals("Wrong number of client names returned.", 55, results.length); }
public void testRegisterNewCustomer() throws Exception{ ClientBean client = new ClientBean(); //register a customer that does not already exist boolean couldRegister = client.register(NEW_CUSTOMER); assertTrue("Was not able to register " + NEW_CUSTOMER, couldRegister); }
public void testRegisterExistingCustomer() throws Exception{ ClientBean client = new ClientBean();
//register a customer that DOES exist boolean couldNotRegister = ! client.register(EXISTING_CUSTOMER); String failureMessage = "Was able to register an existing customer (" + EXISTING_CUSTOMER + "). This should not be " + "possible." assertTrue(failureMessage, couldNotRegister); }
if("java:comp/env/ejb/CustomerManager".equals(name)){ return new MockCustomerManagerHome(); } else{ throw new Error("ClientBean should not lookup any EJBs " + "except CustomerManager"); } } }
[contents of objectReplacement.lst] @base.lst;[A reference to files included in both configurations] MockCustomerManagerHome.java MockCustomerManager.java ObjectReplacement.java.
Nicholas Lesiecki 的 Java Tools for Extreme Programming(与 Rick Hightower 合著)一书中介绍了可以帮助您实现 XP 方法(如单元测试和连续集成)的实用工具。几乎本文中使用的所有工具(JUnit、Cactus 和 Ant)在这个出色的参考中都有所描述。
要了解更多关于模拟对象的信息,请访问 mockobjects.com,或者看看最初开始讨论它的文章“Endo Testing: Unit Testing with Mock Objects”(PDF 格式)。
developerWorks 上有很多关于开始使用 Ant 和 JUnit 的出色文章:请参考 Erik Hatcher 的“Automating the build and test process”(2001 年 8 月)和 Malcolm Davis 的“Incremental development with Ant and JUnit”(2000 年 11 月)。
假如您的测试需要超出单元测试的范围,而达到企业级系统测试领域,请参阅 IBM Performance Management, Testing, and Scalability Services 站点,看看它能提供什么。(该站点包括一个关于企业测试的库。)
假如您在使用 VisualAge for Java 作为 IDE,那么来自 VisualAge 开发者园地的 Unit testing with VAJ 展示了 JUnit 和 VAJ 如何结合成为强大的整体,用最小的努力达到全面的代码测试。
您可以在 IBM developerWorks Java 技术专区上找到数百篇关于 Java 编程每个方面的文章。
关于作者 Nicholas Lesiecki 在互联网正值繁荣时开始接触到 Java,从那时起不断成长为 XP 和 Java 社区的突出人物。Nicholas 目前领导 eBlox Inc. 的旗舰联机目录系统 storeBlox 的开发。除了频繁地在 Tucson JUG 发表演说,他还保持 Jakarta 的 Cactus 工程的积极赞助人身份。Nick 与其它人合著了 Java Tools for Extreme Programming,一本关于在灵活的进程(如 XP)中利用开放源代码构建和测试工具的使用指南手册。Nick 希望感谢 Ron Bodkin、Wes Isberg 和 Vincent Massol 在撰写本文的过程中提供的帮助。请通过 ndlesiecki@apache.org 与 Nick 联系。