(二)利用 Eclipse 进行单元测试

发表于:2007-7-13 16:46

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Michael Nyika    来源:IBM

分享:

场景 1:使用 jMock 模拟接口
  测试 ServiceClass 类中的服务方法十分简单。假定测试要求为证明 runService() 方法并未运行 —— 换言之,返回的布尔结果是 false。在这种情况下,传递给 runService() 方法的 ICollaborator 对象被模拟 为期望调用 executeJob() 方法,并返回除了 “success” 以外的字符串。通过这种方法,确保把布尔字符串 false 返回给测试。
  下面所示的是包含测试逻辑的 ServiceClassTest 类代码。

清单 3. 场景 1 的 ServiceClassTest 类样例代码
               
import org.jmock.Mock;
import org.jmock.cglib.MockObjectTestCase;
public class ServiceClassTest extends MockObjectTestCase {
 private ServiceClass serviceClass;
 private Mock mockCollaborator;
 private ICollaborator collaborator;
 
 public void setUp(){
  serviceClass = new ServiceClass();
  mockCollaborator = new Mock(ICollaborator.class);
 }
 
 public void testRunServiceAndReturnFalse(){
  mockCollaborator.expects(once()).method\
              ("executeJob").will(returnValue("failure"));
  collaborator = (ICollaborator)mockCollaborator.proxy();
  boolean result = serviceClass.runService(collaborator);
  assertFalse(result);
 }
}
  如果将在各种测试用例中执行公共操作,则在测试中包括 setUp() 方法是一种很好的想法。包括 tearDown() 方法也很不错,但不作严格要求,除非要运行集成测试。
另请注意,使用 jMock 和 RMock,框架将在测试运行结束时或测试运行期间在所有模拟对象中检查所有期望。并不实际需要为每个模拟期望包括 verify() 方法。当作为 JUnit 测试运行时,测试将通过,如下所示:

图 3. 场景 1 测试通过

       
  ServiceTestClass 类将扩展 jMock CGLIB 的 org.jmock.cglib.MockObjectTestCase 类。mockCollaborator 是一个十分简单的 org.jmock.JMock 类。通常,用 jMock 生成模拟对象有两种方法:
• 要模拟接口,则使用 new Mock(Class.class) 方法
• 要模拟具体类,则使用 mock(Class.class, "identifier") 方法
  必须注意的是怎样将模拟代理 传递给 ServiceClass 类中的 runService() 方法。使用 jMock,您可以从已创建的模拟对象(其中期望已经被设定)中提取代理实现。这一点在本文稍后的场景中至关重要,尤其是在涉及 RMock 的场景中。
场景 2:使用 jMock 模拟带有默认构造函数的具体类
  假定 ServiceClass 类中的 runService() 方法仅接受 Collaborator 类的具体实现。jMock 能够确保先前的测试通过而无需 更改期望吗?是的,只要您能够构造简单默认样式的 Collaborator 类。
更改 ServiceClass 类中的 runService() 方法使其反映以下代码。

清单 4. 经过编辑的场景 2 的 ServiceClass 类
               
public class ServiceClass {
 public ServiceClass(){
 //no-args constructor 
 }

public boolean runService(Collaborator collaborator){
 if("success".equals(collaborator.executeJob())){
  return true;
 }
 else{
  return false;
 }
}
}

  ServiceClass 类的 if...else 逻辑分支保持不变(为了清晰起见)。同时,无参数构造函数仍然适用。注,并不总是需要有创造性逻辑,例如 while...do 子句或 for 循环来正确地测试类的方法。只要有针对类使用的对象的方法执行,简单的模拟期望就足以测试那些执行。
您还必须更改 ServiceClassTest 类以匹配场景,如下所示:

清单 5. 经过编辑的场景 2 的 ServiceClassTest 类
               
...
private ServiceClass serviceClass;
 private Mock mockCollaborator;
 private Collaborator collaborator;
 
 public void setUp(){
  serviceClass = new ServiceClass();
  mockCollaborator = mock(Collaborator.class, "mockCollaborator");
 }
 
 public void testRunServiceAndReturnFalse(){
  mockCollaborator.expects(once()).method("executeJob").will(returnValue("failure"));
  collaborator = (Collaborator)mockCollaborator.proxy();
  boolean result = serviceClass.runService(collaborator);
  assertFalse(result);
 }
}

  这里有几点需要注意。第一,runService() 方法签名已经不同于以往。它现在不接受 ICollaborator 接口,而接受具体类实现(Collaborator 类)。就测试框架而言,此更改非常重大(注,虽然在本质上反对多态,但是我们将使用传递具体类的示例(仅供举例之用)。在实际的面向对象的场景中绝对不能这样做)。
第二,模拟 Collaborator 类的方式已经更改。使用 jMock CGLIB 库可以模拟具体类实现。提供给 jMock CGLIB 的 mock() 方法的附加 String 参数被用作创建的模拟对象的标识符。使用 jMock(当然,还有 RMock)时,在单一测试用例内每个模拟对象设置都要求有惟一标识符。这对于在公共的 setUp() 方法中或在实际测试方法内定义的模拟对象来说是正确的。
  第三,测试方法的原始期望并未更改。仍然要求有 false 证明才能使测试通过。这是十分重要的,因为通过展示使用的测试框架足够灵活、可以适应各种输入带来的更改、同时仍然允许获得不变的测试结果,使它们在无法调节输入生成同样的结果时展示了其实际限制。
现在,重新运行作为 JUnit 测试的测试。测试将通过,如下所示:

图 4. 场景 2 测试通过

       

在下一个场景中,情况会变得略微复杂一些。您将使用 RMock 框架来相对缓解一下这种困难的情形。

 

精选软件测试好文,快来阅读吧~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号