`

测试驱动开发与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月之前,次文档将一直处于更新状态,随时完善和维护此文档

相关推荐

    EasyMock介绍和使用

    2. **测试驱动开发(TDD)**:在TDD过程中,开发者先编写测试,然后实现满足测试的代码。EasyMock可以帮助快速搭建测试环境,验证代码功能。 3. **复杂逻辑验证**:对于有复杂条件判断或流程控制的代码,EasyMock...

    测试驱动开发RESTService+SwingUI必备jar包套装

    这是一个用来使用TDD(测试驱动开发)来开发REST Service 以及 Java UI的一套jar包,其中easymock包和junit包是TDD必备的,其他两个包根据开发需要酌情添加,也可更换成其他的。第一次做这种开发的时候,配包很麻烦...

    java测试源码-tdd-java-ch05-design-my-version:Java测试驱动开发第5章

    【Java测试驱动开发第5章】是关于使用TDD(测试驱动开发)方法在Java环境中进行软件设计的一个章节。TDD是一种敏捷开发实践,强调先编写测试,再编写实现代码,以此来确保代码质量并降低维护成本。在这个章节中,...

    EasyMockTest

    1. 测试驱动开发(TDD):在编写实际代码之前,先编写测试用例,利用EasyMock模拟依赖对象。 2. 降低测试复杂性:对于依赖外部系统或资源的组件,可以使用EasyMock模拟这些依赖,使测试更加可控。 3. 隔离测试:通过...

    Manning.Test.Driven.TDD.and.Acceptance.TDD.for.Java.Developers

    《测试驱动开发与验收测试驱动开发 for Java 开发者》是Manning出版社的一本专业书籍,专注于Java领域的测试驱动开发(TDD)和验收测试驱动开发(ATDD)。这本书为Java开发者提供了一套完整的测试策略和实践方法,...

    新利用驱动程序和桩程序进行单元测试

    - **使用测试驱动开发(TDD)**:先写测试,再写实现,有助于发现设计问题。 - **持续集成**:将测试自动化,集成到构建流程中,实时监控代码质量。 在Java环境中,可以利用如下的工具和库: - **JUnit**:流行的...

    agile java-驱动测试开发的编程技术-带超详细书签目录.pdf

    《敏捷Java:驱动测试开发的编程技术》是一本专注于Java编程和敏捷开发实践的书籍,其核心在于如何通过测试驱动开发(TDD)提升代码质量和项目效率。这本书以超详细的书签目录为读者提供了便捷的导航,使得学习过程...

    单元测试资料

    在单元测试中,我们首先需要理解测试驱动开发(TDD)的概念。这是一种先写测试,再写实现的开发模式,它鼓励开发者在编写功能代码之前,先编写测试用例,确保新功能的正确性。TDD有助于提高代码质量,减少bug,并且...

    Manning.-.JUnit.in.Action.2nd.Edition_英文版PDF

    本书由资深开发人员撰写,详细介绍了如何有效地利用JUnit进行测试驱动开发(TDD)和行为驱动开发(BDD),帮助读者提升代码质量和可维护性。 在Java开发中,单元测试是确保代码质量的重要环节。JUnit作为Java领域最...

    TDD实用jar

    测试驱动开发(Test-Driven Development,简称TDD)是一种软件开发方法,强调在编写实际功能代码之前先编写测试代码。这种做法有助于确保软件的质量,并且能够及时发现和修复错误。在"EasyMock/JMock/MockObjects"这...

    单元测试 单元测试 java

    1. **测试驱动开发(TDD)** - TDD是一种软件开发实践,开发者首先编写测试,然后编写实现代码,以使测试通过。这有助于确保代码一开始就满足需求,并鼓励编写高质量的代码。 2. **JUnit框架基础** - `@Test`注解:...

    java测试代码

    3. **测试驱动开发(TDD)**:这是一种开发方法论,强调先编写测试,再编写实现代码。这有助于确保代码从一开始就满足需求,并且提高了代码质量。 4. **异常处理测试**:测试代码应覆盖可能抛出的异常情况,确保...

    Mock.java.zip

    Mock Objects是测试驱动开发(TDD)和行为驱动开发(BDD)中的关键概念,它们允许我们隔离被测试代码,以便更好地控制测试环境并验证代码行为。 `Mock.java-idea-01`可能是一个项目或示例,展示了如何在IntelliJ ...

    Java单元测试Java单元测试Java单元测试Java单元测试

    8. **测试驱动开发(TDD)**:这是一种开发实践,先编写测试,再编写满足这些测试的代码。TDD有助于确保代码一开始就满足需求,并减少回归错误。 9. **Mock框架比较**:除了Mockito,还有EasyMock、PowerMock等框架,...

    Pragmatic Unit Testing-Java

    5. **测试驱动开发(TDD)**:《Pragmatic Unit Testing》倡导测试驱动开发的方法,即先写测试,再编写满足这些测试的代码。这有助于保证代码质量,同时促进更好的设计。 6. **Mocking和Stubbing**:在复杂的系统中,...

    junit 学习资料

    10. **测试驱动开发(TDD)**:理解 TDD 的原则和实践,先编写测试,再编写使测试通过的代码,可以提高代码质量,减少错误。 11. **持续集成**:将 JUnit 测试集成到持续集成工具(如 Jenkins, Travis CI, GitHub ...

Global site tag (gtag.js) - Google Analytics