IBM有奖问答 精美礼品等你来拿 详情请见:http://bbs.51testing.com/forum-231-1.html

使用模拟对象(Mock Object)技术进行测试驱动开发

上一篇 / 下一篇  2009-11-05 17:33:17 / 个人分类:前沿技术

fi?wn*I1` Jv.`0  测试驱动开发是敏捷开发中重要部分。在现实项目中,开发人员通常希望减少对其它模块的依赖,把测试的单元与系统其它单元隔离。本文介绍敏捷开发并探讨测试驱动开发的重要性。对 mock 技术进行理论分析,并结合当前流行的 mock 软件如 jMock 和 EasyMock 等,展示测试驱动开发实例并进行比较。51Testing软件测试网6h\~/A6TEE

51Testing软件测试网'R+M*YFey

敏捷开发

)q$m$AY7E0

7Q"DIG hS5i@3K0  敏捷软件开发又称敏捷开发,是一种从上世纪 90 年代开始引起开发人员注意的新型软件开发方法。和传统瀑布式开发方法对比,敏捷开发强调的是在几周或者几个月很短的时间周期,完成相对较小功能,并交付使用。在项目周期内不断改善和增强。51Testing软件测试网"Q6bew(UT

51Testing软件测试网T,t$xw{0D:x

  2001 年初,在美国犹他州雪鸟滑雪胜地,17 名编程大师分别代表极限编程、Scrum、特征驱动开发、动态系统开发方法、自适应软件开发、水晶方法、实用编程等开发流派,发表“敏捷软件开发”宣言。其内容主要包括:

x"^*^~On0

`7h:Em'M!{POD0  * 人和交互重于过程和工具;

_2Jyp`L3HA051Testing软件测试网[h!L|5s@ B

  * 可以工作的软件重于求全责备的文档;

E.R Q$Y{"?M051Testing软件测试网'h,JF#qDT,uq[6T

  * 客户协作重于合同谈判;51Testing软件测试网 t VcK#c:qYf$N

51Testing软件测试网mz.TU*E'h

  * 随时应对变化重于循规蹈矩;

l mb(b(H0

#Tl7{do0  可见在敏捷软件开发中,交付高质量的软件是非常重要的。只有交付可以工作的软件,开发人员才能不断地完成更多功能,而不是将大部分时间投入在修复软件产品缺陷 (Bug) 。所以如何提高交付软件的质量,在敏捷开发实施过程非常重要。

({]1bZVSA#b0


51Testing软件测试网o5J?a*{v

51Testing软件测试网B+h7_.Hz*i

"yPc4[6b)m0测试驱动开发51Testing软件测试网)At(d~&Zl

;luA ^ v`fq0  测试驱动开发,它是敏捷开发的最重要的部分。方法主要是先根据客户的需求编写测试程序,然后再编码使其通过测试。在敏捷开发实施中,开发人员主要从两个方面去理解测试驱动开发。

7Ik#]2A-g`(w'r_+T0

c J0Z&a#?0  * 在测试的辅助下,快速实现客户需求的功能。通过编写测试用例,对客户需求的功能进行分解,并进行系统设计。我们发现从使用角度对代码的设计通常更符合后期开发的需求。可测试的要求,对代码的内聚性的提高和复用都非常有益。51Testing软件测试网J7^#l)o0~}Lb!v

a/_ N#@*Sw0  * 在测试的保护下,不断重构代码,提高代码的重用性,从而提高软件产品的质量。可见测试驱动开发实施的好坏确实极大的影响软件产品的质量,贯穿了软件开发的始终。51Testing软件测试网JBr4u4I}m

S)n1t|c1r4tjjD0  在测试驱动开发中,为了保证测试的稳定性,被测代码接口的稳定性是非常重要的。否则,变化的成本就会急剧的上升。所以,自动化测试将会要求您的设计依赖于接口,而不是具体的类。进而推动设计人员重视接口的设计,体现系统的可扩展性和抗变性。

M%U&u%zI7J0


:e?"x#m n6]A WGA051Testing软件测试网:_/o;]2w?0z{

OpbaZ%^0利用伪对象 (Mock Obect) 实现接口测试51Testing软件测试网/SKF`2E0I t_

51Testing软件测试网!~Sdp7^$kx `,Y

  在实施测试驱动开发过程中,我们可能会发现需要和系统内的某个模块或系统外某个实体交互,而这些模块或实体在您做单元测试的时候可能并不存在,比如您遇到了数据库,遇到了驱动程序等。这时开发人员就需要使用 MO 技术来完成单元测试。51Testing软件测试网mZ3['K}'qd

51Testing软件测试网TPK}v:YRtcTI

  最开始,Mock Object 是完全由测试者自己手工撰写的。在这里我们可以举个简单的例子。

g-@2c M0mt3t051Testing软件测试网7h%Q1]_8os

  我们有一个移动数字电视卡的接口程序。51Testing软件测试网-n)?GGp+[

rJW5pinF Q0清单 1. VideoCardInterface 代码

yUV$X)QBUyBS0

public interface VideoCardInterface { 

  public void open(); 

  public void changeChannel(int i); 

  public void close(); 

  public Byte[] read(); 

  public boolean fault(); 
 }
51Testing软件测试网 |9f p_8x%e#^

n*[2Y.mi0  下面是每个方法的功能说明:

vi?{|8T0

b:T.T#hi:N6p0  * open 打开移动数字电视卡。

,u#\pF#P {y051Testing软件测试网$hA#m gNW#S

  * changeChannel 切换移动数字电视频道。必须在打开之后才可以正常工作,否则就提示错误信息。

s$|*B3d1]0

nKz0V d"a0  * close 关闭移动移动电视卡。必须在打开之后才可以正常工作,否则就提示错误信息。

T.F1b*f poY B051Testing软件测试网s;W$i9Q+U5j

  * read 读取字节流。必须在打开之后才可以正常工作,否则就提示错误信息。51Testing软件测试网t-L(l6~ S@q

s,bd7V{,z0  * fault 显示当前工作状态。

+t X+DG ZJGlx051Testing软件测试网9n3] d)qq/k,cU

  由于相对应的硬件开发工作还没有完成,我们无法基于这样的接口程序进行实际的测试。所以开发人员基于接口,实现了部分移动电视卡的逻辑。51Testing软件测试网Q6BKj `~XUb}O

51Testing软件测试网,GU1XO0y5\ `!] n

清单 2. MockVCHandler 代码51Testing软件测试网7sX/LK@xG.o:C-n

51Testing软件测试网#D J'} tx"g

public class MockVCHandler implements VideoCardInterface { 

  private boolean initialized = false; 
  private boolean error = false; 
  private int channel; 
  private static final int DEFAULTCHANNEL = 1; 

  public void open() { 
   initialized = true; 
   channel = DEFAULTCHANNEL; 
  } 

  public void changeChannel(int i) { 
   if (!initialized) { 
    Assert.fail("Trying to change channel before open"); 
   } 
   if (i <= 0) { 
    Assert.fail("Specified channale is out-of-range"); 
   } 
   this.channel = i; 
  } 

  public void close() { 
   if (!initialized) { 
    Assert.fail("Trying to close before open"); 
   } 
  } 

  public Byte[] read() { 
   if (!initialized) { 
    Assert.fail("Trying to read before open"); 
    return null; 
   } 
   if (channel > 256) { 
    error = true; 
    Assert.fail("Channel is out-of-range"); 
   } 
   return new Byte[] { '0', '1' }; 
  } 

  public boolean fault() { 
   return error; 
  } 
 }

K8W?z3kK0

OPoln~'o0  通过以上的实现,我们可以测试每个动作之间的先后逻辑关系,同时可以测试数据流读取出错的逻辑。如果测试人员进一步对数据流进行测试。还可以自我生成一段二进制字节流并进行测试。通过以上的实现,我们可以大大加快开发进度。在传统开发流程中,软件开发和测试不得不等大部分硬件都已经可以使用的条件下才可以开发测试。使用 MO 技术,使得硬件和软件在一定程度上可以同步开发和测试。

X!R O]:l4rV [*{ c$B0

,F.zA wD7]R0  但是测试人员完全依靠自己实现这些类,不可避免的会带来测试用例编写效率低下和测试用例编写困难的弊病,甚至可能会影响 XP 实践者“测试先行”的激情。此时,各种各样帮助创建 Mock Object 的工具就应运而生了。目前,在 Java 阵营中主要的 Mock 测试工具有 jMock,MockCreator,MockRunner,EasyMock,MockMaker 等,在微软的 .Net 阵营中主要是 NMock,.NetMock,Rhino Mocks 和 Moq 等。

QM2k,R H0e0


u5Z Dw,S[!{a(@0

k-UI:x7t,i I V0

:mi9Y{;Z6Mu0jMock 框架介绍51Testing软件测试网 Ud8g-M/_

r lg?*K-Y Tmn p0  总体上来说,jMock 是一个轻量级的模拟对象技术的实现。它具有以下特点:

H?{z2^q$@p1v0

*Tb f7PG!n0  * 可以用简单易行的方法定义模拟对象,无需破坏本来的代码结构表;

"M mnG*Q'\051Testing软件测试网_BY@yQ;r2m

  * 可以定义对象之间的交互,从而增强测试的稳定性;51Testing软件测试网v"x2H+zQK#e\

51Testing软件测试网7G4T G] QZC2j

  * 可以集成到测试框架;51Testing软件测试网4F6CRZ;yPB)Uf

!e't2td {`'n;z*P0  * 易扩充;51Testing软件测试网-Y5k4Qa7ZL p

dW2j5h C_ Z,o0  与大多数 MOCK 框架一样,我们可以在 IDE 中使用并进行开发。本文以最常用的 Eclipse 为例。51Testing软件测试网M]5Kt"HJ

51Testing软件测试网T1^]4xD5nB0z

  下载 jMock51Testing软件测试网B,VA)Q7},_U

51Testing软件测试网B*x)k yr1j S9r

  在 jMock 官方网站,我们可以下载当前稳定版本 jMock2.5.1 。

/kgb AAZ+c Sf0

m.M*G?~.R6d}0  配置类路径

~VIQl051Testing软件测试网 Yr^/Dwlg;U"]

  为了使用 jMock 2.5.1,您需要加入下面的 JAR 文件到当前的类路径。51Testing软件测试网9R1?4U8M{

51Testing软件测试网q!f j R}'c:xB

  * jmock-2.5.1.jar51Testing软件测试网iRJmDT

51Testing软件测试网4f,y#V8r%Q

  * hamcrest-core-1.1.jar51Testing软件测试网4`&|%dWTn W

51Testing软件测试网n.~%F&@/^Q X

  * hamcrest-library-1.1.jar

(N+ZM&A`O(f051Testing软件测试网&Q&P:v0m.Se;p-g

图 1. 已添加到 TestingExample 项目中 jMock 的 JAR 文件
GWC5F+d%LQ7|S0

A4~,p F\0c0

图 1. 已添加到 TestingExample 项目中 jMock 的 JAR 文件

y+\-]!tj9q_0

:BA1{y'eR/^0  使用 jMock 模拟接口

!}x+U"IXK:u0

?#O&{;h9r0  我们首先必须引入 jMock 的类,定义我们的测试类,创建一个 Mockery 的对象用来代表上下文。上下文可以模拟出对象和对象的输出,并且还可以检测应用是否合法。

-p k5I#{*n M0

E!l4u0vG(p@0

import org.jmock.Mockery; 
 import org.jmock.Expectations; 

 public class AJmockTestCase { 
 
  Mockery context = new Mockery(); 

 }
51Testing软件测试网T zYC9M I

  然后我们创建一个 calcService 去模拟 ICalculatorService 接口。在这里我们以 add() 方法为例,我们针对 add() 方法定义预期值 assumedResult 。之后我们去调用 add(1,1) 时,就可以得到预期值。

Yqrerr#t%x gq0

// set up 
  final ICalculatorService calcService = context.mock(ICalculatorService.class); 
  
  final int assumedResult = 2; 
  
        // expectations 
        context.checking(new Expectations() {{ 
          oneOf (calcService).add(1, 1); will(returnValue(assumedResult)); 
 }});

\mB&}4j oy051Testing软件测试网*e3V7_-n']P9W f

  清单 3 和 4 分别显示了 ICalculatorService 和 AJmockTestCase 的代码。

Re!] T6X s051Testing软件测试网` gd2x@hw8MG+}

清单 3. ICalculatorService 代码

3UGcDx4U4ID"uf0

+Zu#z'zu*HgN&{,}0

public interface ICalculatorService { 

  public int add(int a, int b); 

 }
51Testing软件测试网PmCz` n ~f&j

51Testing软件测试网 a!@jI.oo1kN0a,J

清单 4. AJmockTestCase 代码

DA+GR8vsrE0

%k.F;Mk!Nr ]"WPq LC0

import org.jmock.Mockery; 
 import org.jmock.Expectations; 

 public class AJmockTestCase { 

  Mockery context = new Mockery(); 

  public void testCalcService() { 

   // set up 
   final ICalculatorService calcService = context 
     .mock(ICalculatorService.class); 

   final int assumedResult = 2; 

   // expectations 
   context.checking(new Expectations() { 
    { 
     oneOf(calcService).add(1, 1); 
     will(returnValue(assumedResult)); 
    } 
   }); 

   System.out.println(calcService.add(1, 1)); 

  } 

 }
51Testing软件测试网{%cEy*m'm`6u5xIy/c

51Testing软件测试网9@m Sr Ep

  在 jMock 中,开发人员可以按照下面的语法定义预期值,从而实现更复杂的应用。例如我们可以模拟底层驱动程序的输出,在上层应用程序中使用这些模拟数据。具体可以参考 jMock 的官方网站。51Testing软件测试网9]_MK'P v

51Testing软件测试网0T{@+W xeW-R z

invocation-count (mock-object).method(argument-constraints); 
    inSequence(sequence-name); 
    when(state-machine.is(state-name)); 
    will(action); 
 then(state-machine.is(new-state-name));

Inv+zlHA Z,b0


3f4hEX$Ko051Testing软件测试网:? M7Y7d\|

51Testing软件测试网3I;hk7x[.^b'Mx'Oh

EasyMock 框架介绍

}q:y {Z%x-w-@7?E[051Testing软件测试网3~%jLH G5bX

  在实际开发中,不少开发人员也使用 EasyMock 来进行测试驱动开发。 EasyMock 具有以下的特点

PSoj-y1}f051Testing软件测试网LUJ,N T

  * 在运行时 (runtime) 改变方法名或参数顺序,测试代码不会破坏;

^P%@t IJ4u-gh9|{051Testing软件测试网/`R(L3M;_r

  * 支持返回值和异常;51Testing软件测试网J \$F} yMS

51Testing软件测试网DI Z Pux

  * 对于一个或多个虚拟对象,支持检查方法调用次序;51Testing软件测试网L.Da OY2`3D

$?*])J ]$e5i'\3zD+_0  * 只支持 Java 5.0 及以上版本;51Testing软件测试网9L|Tkb ^C7A

k)FI"h&z^ e4eZ0  与大多数 MOCK 框架一样,我们可以在 IDE 中使用并进行开发。本文以最常用的 Eclipse 为例。

vmf,h#H/Z0

#?.RX/a_"x"z0  下载 EasyMock

'^ Z2Fq ^0

'dZyJ,L0  在 EasyMock 官方网站,我们可以下载当前稳定版本 EasyMock2.4 。

3sX!Fw8f ` }KN0

w:x&GeC L9Ha%c0  配置类路径51Testing软件测试网.t.Eb#SI9Br@

-wS(d[;uaAMr0  为了使用 EasyMock 2.4,您需要加入下面的 JAR 文件到当前的类路径。

/T8Z-m4[7vd7SZ7s051Testing软件测试网/l Uix)Y1C JaF

  * easymock.jar

TJ_ Y8B,P0

|#j-r)l xE%y[0图 2. 已添加到 TestEasyMock 项目中 EasyMock 的 JAR 文件51Testing软件测试网Jh~ X5j

[i:]?.L1j0

图 2. 已添加到 TestEasyMock 项目中 EasyMock 的 JAR 文件

9EX8I{#G,r0

[oP`RT }R3s0使用 EasyMock 模拟接口51Testing软件测试网$N8fX8]'W8Pj"P(U,OB

51Testing软件测试网7A:[rNu P

清单 5. ILEDCard 代码

j5tx6Gh s9u"Vl0

public interface ILEDCard { 
  String getMessage(); 

  void setMessage(String message); 
 }

Ed8E'N9|051Testing软件测试网*g.Z\ ci5h8^w

清单 6. LED 代码51Testing软件测试网wd!pUs$J:Co

51Testing软件测试网!oJw U*h+t!E#^

public class LED { 
  private ILEDCard ledCard; 

  public LED(ILEDCard ledCard) { 
   this.ledCard = ledCard; 
  } 

  public String ShowMesage() { 
   return this.ledCard.getMessage(); 
  } 

  public void setMessage(String message) { 
   this.ledCard.setMessage(message); 
  } 
 }

e$m8M S,O-du+Q0  我们首先创建一个 Mock 的对象 mockLEDCard 来代表 LED 卡的行为,并初始化 LED 对象。51Testing软件测试网%r1R._(b^"R ` k

protected void setUp() throws Exception { 
      super.setUp(); 
      mockLEDCard = createMock(ILEDCard.class); 
      led = new LED(mockLEDCard); 
 }
51Testing软件测试网eg#H{"]9b"q

  之后我们对 ShowMessage 方法进行测试。

Mc'}6R7Qc0A'l-A0

public void testGetWord() { 
        expect(mockLEDCard.getMessage()).andReturn("This is a EasyMock Test!"); 
        replay(mockLEDCard); 

        led.ShowMesage(); 
        verify(mockLEDCard); 
 }
51Testing软件测试网:}9?']4IS+y.l_7d

51Testing软件测试网BD6bX2q\y"U

  清单 7 显示了完整的代码。51Testing软件测试网5l9h/P@)Z`Z

51Testing软件测试网1~:M r&j6n!hCNe*S

清单 7. AEasyMockTestCase 代码

k-M:e7o+LUc'X051Testing软件测试网"L v0i ZqQ,w5@

import static org.easymock.EasyMock.*; 
 import junit.framework.TestCase; 

 public class AEasyMockTestCase extends TestCase { 

  private LED led; 
  private ILEDCard mockLEDCard; 

  protected void setUp() throws Exception { 
   super.setUp(); 
   mockLEDCard = createMock(ILEDCard.class); 
   led = new LED(mockLEDCard); 
  } 

  protected void tearDown() throws Exception { 
   super.tearDown(); 
  } 

  public void testGetWord() { 
   expect(mockLEDCard.getMessage()).andReturn("This is a EasyMock Test!"); 
   replay(mockLEDCard); 

   led.ShowMesage(); 
   verify(mockLEDCard); 
  } 

  public void testSetWord() { 
   mockLEDCard.setMessage("Another test"); 
   replay(mockLEDCard); 

   led.setMessage("Another test"); 
   verify(mockLEDCard); 
  } 
 }
51Testing软件测试网 b Wcc'rLj bO;K

51Testing软件测试网&G0Y ] u@1b/GA|

  通过上文对 jMock 和 EasyMock 的介绍,我们可以发现 jMock 可以灵活的定义对象的行为。例如 mock.expects(once()).method("method2").with( same(b1), ANYTHING ).will(returnValue(method2Result)); 这点在 EasyMock 里比较难于实现。51Testing软件测试网{b/@Q hg O"Q


,y"j W G7U j6FO&j#}051Testing软件测试网+?7_jafFi8N

51Testing软件测试网2E-q0R-YpD*GtB

Rmock 及其它51Testing软件测试网J s T @F~B/Q

51Testing软件测试网)bUQC`o?/d

  目前比较流行的 mock 工具,还有 RMock, 目前的版本的是 2.0,当使用 jUnit 开发测试用例时,它支持设置-修改-运行-验证这样的工作流。它加强了基于交互和基于状态的测试,同时有更好的测试工作流定义。 Rmock 还可以使用 DynamicSuite 来解决维护 TestSuites 的问题。

t(Nc"j.w QdA0

Kz-lW%|{%I/b0  市场上还有支持各种语言的 Mock object 的框架,如 pMock(Python),NMockLib(C#),Mocha(Ruby),JSMock(JavaScript),mockpp(C++) 。

V]"jQ(Em0


1c NH:KcY[*tl051Testing软件测试网#\(t+u ID&i/Cp#B H8a

51Testing软件测试网acMU~ve]h

结语51Testing软件测试网]'@"P e(a"qj

51Testing软件测试网Pt+[ A P;|p

  在软件开发过程中,开发人员需要注重测试驱动开发,并利用模拟对象的技术来帮助进行测试。许多开发人员不习惯于频繁编写测试。即使需要编写测试,通常都是简单的进行主要功能测试。如果要测试代码的某些难以到达的部分,选择各种 Mock object 的框架可以降低开发测试的复杂度。51Testing软件测试网 M5P `pqME8[)O

51Testing软件测试网2d~L%K5Hj+m Pf6Fe

  同时在硬件相关的程序开发中尤其是驱动开发,应用 Mock 技术将极大地提高软件开发的进度,并且减少代码中的缺陷,大大提高硬件软件兼容性和可靠性。51Testing软件测试网/{(B8xwE e"q ^$_G

51Testing软件测试网byp1F+ZB o

作者信息:51Testing软件测试网pY)da9q i.p

6Ht g%r)^SC0方 世明 (fangshim@cn.ibm.com), 软件工程师, IBM

m!_'s ~jn]9z0

;t}:P^b0原文出处:

FE7P2p"gs3|0

HV EyM?1P.u0http://www.ibm.com/developerworks/cn/java/j-lo-mockobject/51Testing软件测试网8b*Qi4C!^Wr V S

'QduV5HV'tPF0IBM活动:

5Q5mM"B9LE9Tj051Testing软件测试网+SAE$[Od6W

下载IBM珍贵资料 领取RQM试用版光盘>>

ev%Mv l.]0

TAG:

 

评分:0

我来说两句

Open Toolbar