测试驱动开发与EasyMock的使用

测试驱动开发并不是什么时髦的词汇,只不过最近以来一直都被炒得风风火火。其大体思想就是,尽管我们并没有实现定义的接口,但是我们可以通过预先了解到的接口行为为我们现有的程序提供服务了。
 
 
这一点尤其是在系统各个模块在同一时间进行开发时,显得格外有优势,传统情况下,由于别人提供的接口尚未实现,因此该接口的调用者就或者只能够静静等待,知道接口实现,或者先写程序然后在接口实现完成的时候不仅需要测试自己写的方法,还要间接测试别人提供的接口。由于在这种情况下找到充足的理由来说明程序的BUG是在接口实现还是在接口调用程序似乎是件非常烦琐的事情。解决办法就是来个metting,大家一起来看。工期就这么被一次次的延误着。
 
 
但是有了EasyMock之后这些都不再成为难题。我们完全不用等待接口编写者到底什么时候能把接口实现结束,更或者根本不用担心无法找到程序Bug的出处。你需要做的,仅仅就是关注你的程序逻辑以及和业务相关的业务规则,保证你为你的程序所写的每个test case都覆盖到了程序的绝大部分的程序逻辑分支(能全部当然最好不过了,但是好像很难!)和所有的业务逻辑分支。
 
 
说到测试驱动开发就很难跟EasyMock划清界限。在Java的先行测试开发中,EasyMock和JMock是最常见的两个工具,都是测试驱动开发的利器,其中EasyMock开发较早。程序员们对EasyMock的关注热情好像要远远胜于JMock。me too!
EasyMock是放在sourceforge上的一个开源项目,可以自由下载(官方网站: http://www.easymock.org/Downloads.html ),从那里下载到对应版本的压缩包后可以直接解压。 如果你的工程使用的是Maven2进行管理,那么要想在命令行也能够执行测试用例,还需要将EasyMock加入到本地仓库中(运行类似如下命令)

E:\easymock2.2>mvn install:install-file -DgroupId=easymock -DartifactId=easymock -Dversion=2.2 -Dpackaging=jar -Dfile=/easymock.jar

 
默认情况下,它只能在Java 5.0以上的版本中运行。EasyMock不仅可以测试定义的接口,对于普通类,EasyMock也提供了支持,不过你需要从他的官方网址下载扩展包:easymockclassextension.jar
由于EasyMock采用了动态字节码生成机制,为了能够保证测试用例正常运行,我们还需要cglib-2.2_beta1.zip 和cglib-nodep-2.2_beta1.zip两个资源文件。cglib全称是Code Generation Library,它可以用来动态继承Java类或者实现接口,很多知名的开源项目中用到了它,譬如Hibernate,Spring之类用它来实现动态代理。(官方网址: http://cglib.sourceforge.net/),由于它是用于在JUnit环境下测试的包,所以在实际使用的时候还需要添加JUnit.jar
 
 
EasyMock相关概念介绍:
————Method、Arguments、InvocationExpectedInvocation、actualMockControl、Mockreset()、replay()、verify()
 
 
method、arguments,invocation这三个概念有从属关系,method代表要模拟的类的一个方法,arguments是这个方法的入口参数,invocation代表一次模拟类的某 个方法的调用,它包含一个method,若干argument(但是在这里不包括返回值)。在EasyMock中有Invocation这个类,含有 Object[] arguments、Method method、Object mock参数。
 
 
ExpectedInvocation,actual前者是代表一次预期的方法调用,这里的预期是指加入了Matcher(s)的Invocation,不仅要具有Invocation的特征,还要加 上对其入口参数的检验器(Matcher),这一概念的引入是为了保证可以判断类似数组这种对象的比较关系,或者为入口参数设定合法条件(不仅是简单的相 等,还有大于等于,字符串的endwith等,用户只要按照它的规则,也可以自己制作专门的matcher)。与其相关的类有 ExpectedInvocation、ExpectedInvocationAndResult、 ExpectedInvocationAndResults,后面两个类加入了指定的返回值,是对有返回值的函数适用的。 而后面的actual是经常出现在EasyMock源代码中作为参数使用的单词,用于代表replay过程中的一次实际的方法调用,和Invocation属于一种概念。
 
 
MockControl、Mock
MockControl是控制类,他负责建立整个框架所需的资源,其成员behavior和RecordState state用于保存方法调用的序列,一个control可以同时管理多个mock。Mock对象对应一个你需要测试的待测试类,它会自动建立 JavaProxyFactory<toMock>,再由JavaProxyFactory建立Proxy;同时建立的还有 ObjectMethodFilter(他持有一个MockInvocationHandler对象,对hashCode()、equals()、 toString()三个方法进行判断)和MockInvocationHandler(他持有一个control对象,似乎是用于添加 Invocation序列的)。
 
 
reset()、replay()、verify()
reset()方法是将control对象复位,其内部现实是靠新建behavior和state两个对象完成的。replay()是结束录制过 程,他会调用RecordState.closeMethod()方法来完成大部分工作。verify()是用于在录制和回放两个步骤完成之后进行预期和 实际结果的检查。
 

mock的种类
EasyMock提供了三种Mock类型:StrictMock、NiceMock、Mock。
 
种类                                                    生成函数检查顺序 检查方法是否调用 对未说明的方法调用 
Mock createMock()                                  否                                    是                     抛出异常 
NiceMock createNiceMock()                 否                                    是                返回0、null,不抛出异常 
StrictMock createStrictMock()                是                                    是                     抛出异常 
 
 

举例来说:
 
1.有返回值类型的接口测试

假设我们需要在程序中用到一个接口,接口定义如下:

package org.danlley.common;
public interface ForMock{
    public String doSomething();
}

 
 
 
接口的调用类定义如下:
java 代码
  1. package org.danlley.common;   
  2. public class MockClassCalller{   
  3.     ForMock needtoMock;   
  4.     public String IwantCallDoSomething()throws Exception{   
  5.         String strs=needtoMock.doSomething();   
  6.         System.out.println(strs);   
  7.         return strs;   
  8.     }   
  9.     public ForMock getNeedtoMock(){   
  10.         return this.needtoMock;   
  11.     }   
  12.     public void setNeedtoMock(ForMock needtoMock){   
  13.         this.needtoMock=needtoMock;   
  14.     }   
  15. }  
 
 
我们现在为MockClassCalller编写测试用例,由于接口ForMock尚未实现,因此,我们无法获得真是的功能所以我们需要在这里模拟他的实现过程以便我们的测试能够正常运行。
编写的测试用例如下:
java 代码
  1. package org.danlley.common;   
  2. import junit.framework.TestCase;   
  3. import org.junit.Test;   
  4. import static org.easymock.EasyMock.expect;   
  5. import static org.easymock.classextension.EasyMock.*;   
  6. public class MockClassCalllerTest extends TestCase{   
  7.     @Test  
  8.     public void testIwantCallDoSomething(){   
  9.         try{   
  10.             ForMock forMock=createMock(ForMock.class);   
  11.             expect(forMock.doSomething()).andReturn("111111111111111").anyTimes();   
  12.             replay(forMock);   
  13.             MockClassCalller mockCaller=new MockClassCalller();   
  14.             mockCaller.setNeedtoMock(forMock);   
  15.             String mystr=mockCaller.IwantCallDoSomething();   
  16.             System.out.println(mystr);   
  17.             verify(forMock);   
  18.         }catch(Exception e){   
  19.             e.printStackTrace();   
  20.         }   
  21.     }   
  22. }  
 
我们在这个测试用例中用到了JDK5中的静态导入策略,将EasyMock中的所有静态方法静态导入到当前类中。最后运行结果在Eclipse中为:

111111111111111  --此行数据为MockClassCalller所打印的数据
111111111111111  --此行数据为MockClassCalllerTest打印数据

 

说明自始至终,程序都是在以我们expect的结果相一致的情况下运行。可以看出尽管我们并没有实现ForMock中定义的接口,但是我们已经可以通过预先了解到的接口行为为我们现有的程序提供服务了。
 
 
 
 
2.无返回值类型的接口测试:
 
在ForMock中添加接口如下:
java 代码
  1. public void withoutReturn();  
在MockClassCalller中添加方法:
java 代码
  1. public void IwantCallWithoutReturn()throws Exception{   
  2.     needtoMock.withoutReturn();   
  3.     System.out.println("needtoMock.withoutReturn() has been callled ! ");   
  4. }   
向测试类中添加测试代码如下:
java 代码
  1. @Test  
  2. public void testIwantCallWithoutReturn(){   
  3.     try{   
  4.         ForMock forMock=createMock(ForMock.class);   
  5.         forMock.withoutReturn();   
  6.         expect(forMock.doSomething()).andReturn("111111111111111").anyTimes();   
  7.         replay(forMock);   
  8.         MockClassCalller mockCaller=new MockClassCalller();   
  9.         mockCaller.setNeedtoMock(forMock);   
  10.         mockCaller.IwantCallWithoutReturn();   
  11.         verify(forMock);   
  12.     }catch(Exception e){   
  13.         e.printStackTrace();   
  14.     }   
  15. }  
运行结果如下:

111111111111111
111111111111111
needtoMock.withoutReturn() has been callled !

 
 
 
 
 
 
 
 
      特别说明:2007年8月之前,次文档将一直处于更新状态,随时完善和维护此文档

你可能感兴趣的:(spring,Hibernate,.net,JUnit,asp.net)