`
zyl
  • 浏览: 486757 次
社区版块
存档分类
最新评论

EasyMock 2 使用指南

阅读更多

关于单元测试,模拟对象一直是不可缺少的,尤其对于复杂的应用来说。
       这么多的模拟对象框架中,个人觉得比较好用的当属EasyMock了。当然JMock也不错。
       下面简单介绍一下EasyMock 。
     
       EasyMock 2
主要用于给指定的接口提供模拟对象。 <o:p></o:p>

模拟对象只是模拟领域代码直接的部分行为,能检测是否他们如定义中的被使用。使用 Mock 对象,来模拟合作接口,有助于隔离测试相应的领域类。 <o:p></o:p>

创建和维持 Mock 对象经常是繁琐的任务,并且可能会引入错误。 EasyMock 2 动态产生 Mock 对象,不需要创建,并且不会产生代码。 <o:p></o:p>

有利的方面: <o:p></o:p>

不需要手工写类来处理 mock 对象。 <o:p></o:p>

支持安全的重构 Mock 对象:测试代码不会在运行期打断当重新命名方法或者更改方法参数。 <o:p></o:p>

支持返回值和例外。 <o:p></o:p>

支持检察方法调用次序,对于一个或者多个 Mock 对象。 <o:p></o:p>

不利的方面: 2.0 仅使用于 java 2 版本 5.0 或者以上
    

    以一个例子来说明如何使用EasyMock:
   假设有一个合作接口Collaborator:
           

package org.easymock.samples;<o:p></o:p>
<o:p> </o:p>
public interface Collaborator {<o:p></o:p>
    void documentAdded(String title);<o:p></o:p>
    void documentChanged(String title);<o:p></o:p>
    void documentRemoved(String title);<o:p></o:p>
    byte voteForRemoval(String title);<o:p></o:p>
    byte[] voteForRemovals(String[] title);<o:p></o:p>
}<o:p></o:p>

我们主要的测试类为:

package org.easymock.samples;<o:p></o:p>
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class ClassUnderTest {
    private Set<Collaborator> listeners = new HashSet<Collaborator>();
    private Map<String, byte[]> documents = new HashMap<String, byte[]>();
    public void addListener(Collaborator listener) {
        listeners.add(listener);
    }
    public void addDocument(String title, byte[] document) {
        boolean documentChange = documents.containsKey(title);
        documents.put(title, document);
        if (documentChange) {
            notifyListenersDocumentChanged(title);
        } else {
            notifyListenersDocumentAdded(title);
        }
    }
    public boolean removeDocument(String title) {
        if (!documents.containsKey(title)) {
            return true;
        }
        if (!listenersAllowRemoval(title)) {
            return false;
        }
        documents.remove(title);
        notifyListenersDocumentRemoved(title);
        return true;
    }
    public boolean removeDocuments(String[] titles) {
        if (!listenersAllowRemovals(titles)) {
            return false;
        }
        for (String title : titles) {
            documents.remove(title);
            notifyListenersDocumentRemoved(title);
        }
        return true;
    }
    private void notifyListenersDocumentAdded(String title) {
        for (Collaborator listener : listeners) {
            listener.documentAdded(title);
        }
    }
    private void notifyListenersDocumentChanged(String title) {
        for (Collaborator listener : listeners) {
            listener.documentChanged(title);
        }
    }
    private void notifyListenersDocumentRemoved(String title) {
        for (Collaborator listener : listeners) {
            listener.documentRemoved(title);
        }
    }
    private boolean listenersAllowRemoval(String title) {
        int result = 0;
        for (Collaborator listener : listeners) {
            result += listener.voteForRemoval(title);
        }
        return result > 0;
    }
    private boolean listenersAllowRemovals(String[] titles) {
        int result = 0;
        for (Collaborator listener : listeners) {
            result += listener.voteForRemovals(titles);
        }
        return result > 0;
    }
}

第一个Mock 对象<o:p></o:p>

我们将创建test case 并且围绕此理解相关的EasyMock 包的功能。第一个测试方法,用于检测是否删除一个不存在的文档,不会发通知给合作类。
          
<o:p>
package org.easymock.samples;<o:p></o:p>
<o:p> </o:p>
import junit.framework.TestCase;<o:p></o:p>
<o:p> </o:p>
public class ExampleTest extends TestCase {<o:p></o:p>
<o:p> </o:p>
    private ClassUnderTest classUnderTest;<o:p></o:p>
    private Collaborator mock;<o:p></o:p>
<o:p> </o:p>
    protected void setUp() {<o:p></o:p>
        classUnderTest = new ClassUnderTest();<o:p></o:p>
        classUnderTest.addListener(mock);<o:p></o:p>
    }<o:p></o:p>
<o:p> </o:p>
    public void testRemoveNonExistingDocument() {    <o:p></o:p>
        // This call should not lead to any notification<o:p></o:p>
        // of the Mock Object: <o:p></o:p>
        classUnderTest.removeDocument("Does not exist");<o:p></o:p>
    }<o:p></o:p>
}<o:p></o:p>
</o:p>
    对于多数测试类,使用EasyMock 2,我们只需要静态引入org.easymock.EasyMock的方法。      
<o:p></o:p> 

import static org.easymock.EasyMock.*;<o:p></o:p>

import junit.framework.TestCase;<o:p></o:p>

<o:p> </o:p>

public class ExampleTest extends TestCase {<o:p></o:p>

<o:p> </o:p>

    private ClassUnderTest classUnderTest;<o:p></o:p>

    private Collaborator mock;<o:p></o:p>

    <o:p></o:p>

}<o:p></o:p>

     

为了取得Mock 对象,需要:<o:p></o:p>

l         创建Mock 对象从需要模拟的接口<o:p></o:p>

l         记录期待的行为<o:p></o:p>

l         转换到Mock对象,replay状态。<o:p></o:p>

例如:     
<o:p></o:p> 
protected void setUp() {<o:p></o:p>
        mock = createMock(Collaborator.class); // 1<o:p></o:p>
        classUnderTest = new ClassUnderTest();<o:p></o:p>
        classUnderTest.addListener(mock);<o:p></o:p>
    }

<o:p>
 public void testRemoveNonExistingDocument() {<o:p></o:p>
        // 2 (we do not expect anything)<o:p></o:p>
        replay(mock); // 3<o:p></o:p>
        classUnderTest.removeDocument("Does not exist");<o:p></o:p>
    }</o:p>
  

在执行第三步后,mock Collaborator接口的Mock对象,并且期待没有什么调用。这就意味着,如果我们改变ClassUnderTest去调用此接口的任何方法,则Mock对象会抛出AssertionError<o:p></o:p>

        
<o:p></o:p> 
java.lang.AssertionError: <o:p></o:p>
  Unexpected method call documentRemoved("Does not exist"):<o:p></o:p>
    at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)<o:p></o:p>
    at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)<o:p></o:p>
    at $Proxy0.documentRemoved(Unknown Source)<o:p></o:p>
    at org.easymock.samples.ClassUnderTest.notifyListenersDocumentRemoved(ClassUnderTest.java:74)<o:p></o:p>
    at org.easymock.samples.ClassUnderTest.removeDocument(ClassUnderTest.java:33)<o:p></o:p>
    at org.easymock.samples.ExampleTest.testRemoveNonExistingDocument(ExampleTest.java:24)<o:p></o:p>
    ...<o:p></o:p>

增加行为<o:p></o:p>

       让我们开始第二个测试。如果documentclassUnderTest增加,我们期待调用
mock.documentAdded()Mock对象使用document的标题作为参数:<o:p></o:p>
<o:p></o:p> 
 public void testAddDocument() {<o:p></o:p>
        mock.documentAdded("New Document"); // 2<o:p></o:p>
        replay(mock); // 3<o:p></o:p>
        classUnderTest.addDocument("New Document", new byte[0]); <o:p></o:p>
    }<o:p></o:p>
如果classUnderTest.addDocument("New Document", new byte[0])调用期待的方法,使用错误的参数,Mock对象会抛出AssertionError:
<o:p></o:p> 
java.lang.AssertionError: <o:p></o:p>
  Unexpected method call documentAdded("Wrong title"):<o:p></o:p>
    documentAdded("New Document"): expected: 1, actual: 0<o:p></o:p>
    at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)<o:p></o:p>
    at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)<o:p></o:p>
    at $Proxy0.documentAdded(Unknown Source)<o:p></o:p>
    at org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:61)<o:p></o:p>
    at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:28)<o:p></o:p>
    at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)<o:p></o:p>
    ...<o:p></o:p>

同样,如果调用多次此方法,则也会抛出例外:

<o:p></o:p> 
java.lang.AssertionError: <o:p></o:p>
  Unexpected method call documentAdded("New Document"):<o:p></o:p>
    documentAdded("New Document"): expected: 1, actual: 1 (+1)<o:p></o:p>
    at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)<o:p></o:p>
    at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)<o:p></o:p>
    at $Proxy0.documentAdded(Unknown Source)<o:p></o:p>
    at org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:62)<o:p></o:p>
    at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:29)<o:p></o:p>
    at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)<o:p></o:p>
    ...<o:p></o:p>

验证行为<o:p></o:p>

       当我们指定行为后,我们将验证实际发生的。当前的测试将会判断是否Mock对象会真实调用。可以调用verify(mock)来山正是否指定的行为被调用。

<o:p></o:p> 
public void testAddDocument() {<o:p></o:p>
        mock.documentAdded("New Document"); // 2 <o:p></o:p>
        replay(mock); // 3<o:p></o:p>
        classUnderTest.addDocument("New Document", new byte[0]);<o:p></o:p>
        verify(mock);<o:p></o:p>
    }<o:p></o:p>

如果失败,则抛出AssertionError<o:p></o:p>

期待明显数量的调用<o:p></o:p>

到现在,我们的测试只是调用一个简单的方法。下一个测试将会检测是否已经存在document导致mock.documentChanged()调用。为了确认,调用三次

<o:p></o:p> 
public void testAddAndChangeDocument() {<o:p></o:p>
        mock.documentAdded("Document");<o:p></o:p>
        mock.documentChanged("Document");<o:p></o:p>
        mock.documentChanged("Document");<o:p></o:p>
        mock.documentChanged("Document");<o:p></o:p>
        replay(mock);<o:p></o:p>
        classUnderTest.addDocument("Document", new byte[0]);<o:p></o:p>
        classUnderTest.addDocument("Document", new byte[0]);<o:p></o:p>
        classUnderTest.addDocument("Document", new byte[0]);<o:p></o:p>
        classUnderTest.addDocument("Document", new byte[0]);<o:p></o:p>
        verify(mock);<o:p></o:p>
    }<o:p></o:p>

为了避免重复的mock.documentChanged("Document"),EasyMock提供一个快捷方式。可以通过调用方法expectLastCall().times(int times)来指定最后一次调用的次数。

<o:p></o:p> 
 public void testAddAndChangeDocument() {<o:p></o:p>
        mock.documentAdded("Document");<o:p></o:p>
        mock.documentChanged("Document");<o:p></o:p>
        expectLastCall().times(3);<o:p></o:p>
        replay(mock);<o:p></o:p>
        classUnderTest.addDocument("Document", new byte[0]);<o:p></o:p>
        classUnderTest.addDocument("Document", new byte[0]);<o:p></o:p>
        classUnderTest.addDocument("Document", new byte[0]);<o:p></o:p>
        classUnderTest.addDocument("Document", new byte[0]);<o:p></o:p>
        verify(mock);<o:p></o:p>
    }<o:p></o:p>

指定返回值<o:p></o:p>

       对于指定返回值,我们通过封装expect(T value)返回的对象并且指定返回的值,使用方法andReturn(Object returnValue)expect(T value).返回的对象。<o:p></o:p>

例如:<o:p></o:p>

 

<o:p></o:p> 
public void testVoteForRemoval() {<o:p></o:p>
        mock.documentAdded("Document");   // expect document addition<o:p></o:p>
        // expect to be asked to vote for document removal, and vote for it<o:p></o:p>
        expect(mock.voteForRemoval("Document")).andReturn((byte) 42);<o:p></o:p>
        mock.documentRemoved("Document"); // expect document removal<o:p></o:p>
        replay(mock);<o:p></o:p>
        classUnderTest.addDocument("Document", new byte[0]);<o:p></o:p>
        assertTrue(classUnderTest.removeDocument("Document"));<o:p></o:p>
        verify(mock);<o:p></o:p>
    } <o:p></o:p>
<o:p> </o:p>
    public void testVoteAgainstRemoval() {<o:p></o:p>
        mock.documentAdded("Document");   // expect document addition<o:p></o:p>
        // expect to be asked to vote for document removal, and vote against it<o:p></o:p>
        expect(mock.voteForRemoval("Document")).andReturn((byte) -42);<o:p></o:p>
        replay(mock);<o:p></o:p>
        classUnderTest.addDocument("Document", new byte[0]);<o:p></o:p>
        assertFalse(classUnderTest.removeDocument("Document"));<o:p></o:p>
        verify(mock);<o:p></o:p>
    }<o:p></o:p>
取代expect(T value)调用,可以通过expectLastCall().来代替
<o:p></o:p> 
分享到:
评论
1 楼 nhy520 2008-10-14  
很好,呵呵

相关推荐

    Learning EasyMock3.0 By Official Example

    3. `EndecaConceptsGuide.pdf`:Endeca 是一个数据管理平台,这个指南可能涉及到如何在 Endeca 环境下使用 EasyMock。虽然 Endeca 不是 EasyMock 的一部分,但了解如何在特定上下文中使用模拟对象是重要的实践技巧。...

    easymock-3.2.zip

    2. **doc文档**:这部分可能包含了项目的用户指南、开发者指南或设计理念等,帮助开发者更深入地理解EasyMock的工作原理和最佳实践。 3. **示例文档**:这些示例通常是一些简单的代码片段,展示了如何在实际项目中...

    EasyMock 使用方法与原理剖析

    EasyMock单元测试的扩展; EasyMock简介(抽象类接口做测试); EasyMock来进行测试;...EasyMock实践指南; EasyMock使用技巧; EasyMock使用简明手册; EasyMock使用说明; EasyMock使用手记; 用Mock object进行隔离测试;

    easymock-3.1

    文档部分应该包含了详细的使用指南和API参考,帮助你理解和应用EasyMock的各种功能。 总之,EasyMock 3.1是一个强大且灵活的模拟框架,它简化了Java单元测试中的对象模拟过程,提高了测试的效率和覆盖率。通过深入...

    Easymock 3.3.1

    在 Easymock 中,我们可以使用 `EasyMock.createMock()` 方法创建模拟对象。然后,使用 `expect()` 方法设置期望,`replay()` 方法启动模拟对象,使其按照预设的期望执行,最后使用 `verify()` 方法检查模拟对象是否...

    Easy Mock 详解

    EasyMock单元测试的扩展; EasyMock简介(抽象类接口做测试); EasyMock来进行测试;...EasyMock实践指南; EasyMock使用技巧; EasyMock使用简明手册; EasyMock使用说明; EasyMock使用手记; 用Mock object进行隔离测试;

    EasyMock线上项目迁移工具

    在测试过程中,开发者可以使用EasyMock来创建模拟对象,这些对象可以模仿实际对象的行为,如返回预设值、抛出异常等,从而避免了对真实对象的依赖,提高了测试的独立性和效率。通过这个工具,我们可以将线上项目中的...

    jTester使用指南(带书签).pdf

    ### jTester使用指南知识点概述 #### 一、安装与配置jTester **1.1 Maven 用户安装** - **步骤说明**:Maven 用户可以通过在项目的 `pom.xml` 文件中添加 jTester 的依赖来安装 jTester。这一步骤非常简便,只需...

    PowerMock学习指南

    2. **注解使用**:在测试类上使用`@RunWith(PowerMockRunner.class)`注解,以启用PowerMock运行器。同时,如果你需要模拟静态方法,需要添加`@PrepareForTest`注解,指定需要模拟的类。 3. **测试规则**:在测试...

    GMock——groovy下的mock工具

    - `docs`:可能包含 GMock 的文档和用户指南,有助于深入理解和使用。 - `src`:源代码文件,可能包含了 GMock 的实现细节,对于开发者来说是很好的学习资源。 - `samples`:示例代码,可以帮助初学者快速上手。 总...

    hypersnap

    7. easymock教程.pdf:EasyMock是一个Java模拟框架,这个PDF文件可能是EasyMock的使用教程,帮助开发者进行单元测试。 8. YATR瀹炵幇绠€浠_20090422).ppt:这看起来是关于YATR项目的演示文稿,日期表明是2009年4月...

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

    《JUnit in Action 2nd Edition》是一本专为软件开发者设计的指南,旨在深入解析JUnit这一流行的Java单元测试框架。本书由资深开发人员撰写,详细介绍了如何有效地利用JUnit进行测试驱动开发(TDD)和行为驱动开发...

    junit4资料

    ### JUnit4基础知识与实践指南 #### 一、JUnit4简介 JUnit4是JUnit框架的一个重要版本,它为Java开发者提供了强大的单元测试能力。相比于早期版本,JUnit4引入了多种新特性来简化测试代码的编写过程,并提高了测试...

    jmock2.5.1.zip

    jMock 2.5.1.chm文件是一个帮助文档,包含了jMock框架的详细使用指南。通过这个文档,开发者可以学习如何创建和配置模拟对象,设置和验证期待,以及如何在JUnit等测试框架中集成jMock。 首先,创建模拟对象通常需要...

    PowerMock实战手册

    《PowerMock实战手册》是一本专注于使用PowerMock进行单元测试的指南,结合了Junit测试框架和Mockito库,为开发者提供了全面的测试解决方案。在实际的软件开发中,单元测试是确保代码质量的重要环节,它能帮助我们找...

    powermock 资料_杂

    单元测试在软件开发流程中扮演着至关重要的角色,特别是在TDD模式下,单元测试不仅是代码质量的保障,更是设计和重构的指南。然而,在没有模拟框架辅助的情况下,编写单元测试可能是一项耗时且繁琐的任务,需要大量...

    books-powermock-unit-test

    2. **Mocking 和 Stubbing**:了解如何使用 Mock 对象替代实际依赖,以及如何设置 Stub 方法来控制预期的行为。 3. **PowerMock 功能**:学习如何使用 PowerMock 来模拟静态方法、构造函数、final 类和方法、私有...

    powermock-demo:powermock演示

    5. **README.md**:项目简介和使用指南,可能会有如何运行测试的说明。 在实际使用 PowerMock 进行测试时,我们需要注意以下几点: 1. **使用注解**:在测试类上使用 `@RunWith(PowerMockRunner.class)` 注解,以...

    HadoopJavaMock

    4. **README.md**:项目介绍、安装和使用指南,可能包含示例代码和API参考。 5. **LICENSE**:项目许可文件,规定了软件的使用权限和条件。 6. **其他资源**:如图片、字体或其他支持Swing应用运行的文件。 综上所...

Global site tag (gtag.js) - Google Analytics