zoukankan      html  css  js  c++  java
  • EasyMock使用说明

    来自官网的使用说明,原文见http://www.easymock.org/EasyMock2_0_Documentation.html

    1.1. 准备

    大多数的软件系统都不是单独运行的,它们都需要于其他部分系统合作,来完成工作。大多数情况下,我们在进行单元测试时,不会担心其他部分,而是假定它们都会工作良好。如果我们需要考虑其他部分的情况,Mock对象可以帮助我们对某一单元,进行隔离测试。Mock对象将在测试中代替合作者。

    下边的例子是一个Collaborator接口:

    package org.easymock.samples;

     

    public interface Collaborator {

              void documentAdded(String title);

              void documentChanged(String title);

              void documentRemoved(String title);

              byte voteForRemoval(String title);

              byte[] voteForRemovals(String[] title);

    }

                       

    实现了这个接口的类(在这里都是监听器),是一个叫做ClassUnderTest类的合作者:

    public class ClassUnderTest {

              //...

              public void addListener(Collaborator listener) {

                        //...

              }

              public void addDocument(String title, byte[] document) {

                        //...

              }

              public boolean removeDocument(String title) {

                        //...

              }

              public boolean removeDocuments(String[] titles) {

                        //...

              }

    }

                       

    这些代码都可以在easymock.zip的sample.zip中找到,包名为org.easymock.samples

    下面的例子假设你熟悉JUnit测试框架。虽然这些测试使用了JUnit 3.8.1,你也可以使用JUnit 4或TestNG

    1.2. 第一个Mock对象

    我们现在就要写一个测试用例,然后根据它来了解EasyMock包的结构。samples.zip里包含了这个测试的修改版。我们的第一个测试,应该检测,一个不存在的document的removal操作是否会告诉那些合作者。这里的测试,没有定义Mock对象:

    package org.easymock.samples;

     

    import junit.framework.TestCase;

     

    public class ExampleTest extends TestCase {

     

              private ClassUnderTest classUnderTest;

              private Collaborator mock;

     

              protected void setUp() {

                        classUnderTest = new ClassUnderTest();

                        classUnderTest.addListener(mock);

              }

     

              public void testRemoveNonExistingDocument() {

                        // This call should not lead to any notification

                        // of the Mock Object:

                        classUnderTest.removeDocument("Does not exist");

              }

    }

                       

    对于大多数使用EasyMock 2的测试,我们只要使用静态导入org.easymock.EasyMock的所有方法。这是EasyMock 2中唯一没有内在方法,也没有废弃方法的类。

    import static org.easymock.EasyMock.*;

    import junit.framewok.TestCase;

     

    public class ExampleTest extends TestCase {

              private ClassUnderTest classUnderTest;

              private Collaborator mock;

    }

                       

    为了获得Mock对象,我们需要

    • 为我们想使用的接口,创建一个Mock对象。
    • 录制预期的行为,然后
    • 将Mock对象转换到replay(重放)状态

    这里有一个小例子:

    protected void setUp() {

              mock = createMock(Collaborator.class); // 1

              classUnderTest = new ClassUnderTest();

              classUnderTest.addListener(mock);

    }

     

    public void testRemoveNonExistingDocument() {

              // 2 (we do not expect anything)

              replay(mock); // 3

              classUnderTest.removeDocument("Does not exist");

    }

                       

    完成第三步后,mock是一个Collaborator接口的Mock对象,它不希望有任何的调用。这意味着,如果我们改变了ClassUnderTest,调用了接口的任何一个方法,Mock对象就回抛出一个AssertionError:

    java.lang.AssertionError:

      Unexpected method call documentRemoved("Does not exist"):

        at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)

        at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)

        at $Proxy0.documentRemoved(Unknown Source)

        at org.easymock.samples.ClassUnderTest.notifyListenersDocumentRemoved(ClassUnderTest.java:74)

        at org.easymock.samples.ClassUnderTest.removeDocument(ClassUnderTest.java:33)

        at org.easymock.samples.ExampleTest.testRemoveNonExistingDocument(ExampleTest.java:24)

        ...

                       

    1.3. 添加行为

    让我们开始写第二个测试。如果一个document添加到ClassUnderTest中,我们希望mock.documentAdded()会被调用,并把document的标题作为参数。

    public void testAddDocument() {

              mock.documentAdded("New Document"); // 2

              replay(mock); // 3

              classUnderTest.addDocument("New Document", new byte[0]);

    }

                       

    在录制环节(调用replay之前),Mock对象没有办法像Mock对象那样工作,但是它记录了方法调用。在调用replay之后,它就可以使用了。检查是否出现了对期望的方法的调用。

    如果ClassUnderTest.addDocument("New Document", new byte[0])调用了预期的方法,但使用了错误参数,Mock对象会抛出一个AssertionErro:

    java.lang.AssertionError:

      Unexpected method call documentAdded("Wrong title"):

        documentAdded("New Document"): expected: 1, actual: 0

        at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)

        at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)

        at $Proxy0.documentAdded(Unknown Source)

        at org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:61)

        at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:28)

        at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)

        ...

                       

    没有出现的所有预期记录都会表现出来。会把不是预期的调用做完全的比较(这个例子里没有出现)。如果方法调用次数过多,Mock对象也会抱怨:

    java.lang.AssertionError:

      Unexpected method call documentAdded("New Document"):

        documentAdded("New Document"): expected: 1, actual: 1 (+1)

        at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)

        at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)

        at $Proxy0.documentAdded(Unknown Source)

        at org.easymock.samples.ClassUnderTest.notifyListenersDocumentAdded(ClassUnderTest.java:62)

        at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:29)

        at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:30)

        ...

                       

    1.4. 验证行为

    这里有一个我们一直没有处理的问题:如果我们指定一个行为,我们应该验证它是否确实使用了。刚才的测试会在Mock对象没有调用的时候通过,为了严整指定的行为是否使用,我们需要调用verify(mock)。

    public void testAddDocument() {

              mock.documentAdded("New Document"); // 2

              replay(mock); // 3

              classUnderTest.addDocument("New Document", new byte[0]);

              verify(mock);

    }

                       

    如果Mock中的方法没有调用,我们会得到下面的异常:

    java.lang.AssertionError:

      Expectation failure on verify:

        documentAdded("New Document"): expected: 1, actual: 0

        at org.easymock.internal.MocksControl.verify(MocksControl.java:70)

        at org.easymock.EasyMock.verify(EasyMock.java:536)

        at org.easymock.samples.ExampleTest.testAddDocument(ExampleTest.java:31)

        ...

                       

    异常信息包括了所有没有运行的期望。

    1.5. 期待对方法调用特定次数

    到现在为止。我们的测试都只考虑单一方法调用。下一个测试需要测试,有一个已经存在的document添加的时候,是否会引起mock.documentChanged()方法,并使用适当的参数。为了确认。我们检测三次(只是作为例子而已:))。

    public void testAddAndChangeDocument() {

              mock.documentAdded("Document");

              mock.documentChanged("Document");

              mock.documentChanged("Document");

              mock.documentChanged("Document");

              mock.documentChanged("Document");

              replay(mock);

              classUnderTest.addDocument("Document", new byte[0]);

              classUnderTest.addDocument("Document", new byte[0]);

              classUnderTest.addDocument("Document", new byte[0]);

              classUnderTest.addDocument("Document", new byte[0]);

              verify(mock);

    }

                       

    为了避免重复输入mock.documentChanged("Document"),EasyMock提供了一个捷径。我们在expectLastCall()后边使用times(int times)方法,指定调用的次数。代码如下:

    public void testAddAndChangeDocument() {

              mock.documentAdded("Document");

              mock.documentChanged("Document");

              expectLastCall().times(3);

              replay(mock);

              classUnderTest.addDocument("Document", new byte[0]);

              classUnderTest.addDocument("Document", new byte[0]);

              classUnderTest.addDocument("Document", new byte[0]);

              classUnderTest.addDocument("Document", new byte[0]);

              verify(mock);

    }

                       

    如果方法调用了太多次,我们会获得一个异常,告诉我们方法调用了过多次。在第一个方法调用超过次数的时候,就会立即产生错误。

    java.lang.AssertionError:

      Unexpected method call documentChanged("Document"):

        documentChanged("Document"): expected: 3, actual: 3 (+1)

              at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)

              at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44)

              at $Proxy0.documentChanged(Unknown Source)

              at org.easymock.samples.ClassUnderTest.notifyListenersDocumentChanged(ClassUnderTest.java:67)

              at org.easymock.samples.ClassUnderTest.addDocument(ClassUnderTest.java:26)

              at org.easymock.samples.ExampleTest.testAddAndChangeDocument(ExampleTest.java:43)

                       

    如果调用次数比预期少,verify(mock)就会抛出一个AssertionError:

    java.lang.AssertionError:

      Expectation failure on verify:

        documentChanged("Document"): expected: 3, actual: 2

              at org.easymock.internal.MocksControl.verify(MocksControl.java:70)

              at org.easymock.EasyMock.verify(EasyMock.java:536)

              at org.easymock.samples.ExampleTest.testAddAndChangeDocument(ExampleTest.java:43)

        ...

                       

    1.6. 指定返回值

    为了指定返回值,我们在expect(T value)中封装了预期的调用,在它返回的对象中,使用andReturn(Object returnValue)指定了返回值。

    作为例子,我们检测删除document的流程。如果ClassUnderTest得到了一个删除document的调用。它会询问所有的合作者的表决,来调用byte voteForRemoval(String title)来进行删除。正的返回值表示同意删除。如果所有值的和是正数,就删除document并调用所有合作者的documentRemoved(String title):

    public void testVoteForRemoval() {

              mock.documentAdded("Document"); // expect document addition

              // expect to be asked to vote for document removal, and vote for it

              expect(mock.voteForRemoval("Document")).addReturn((byte) 42);

              mock.documentRemoved("Document"); // expect document removal

              replay(mock);

              classUnderTest.addDocument("Document", new byte[0]);

              assertTrue(classUnderTest.removeDocument("Document"));

              verify(mock);

    }

     

    public void testVoteAgainstRemoval() {

              mock.documentAdded("Document"); // expect document addition

              // expect to be asked to vote for document removal, and vote against it

              expect(mock.voteForRemoval("Document")).andReturn((byte) -42);

              replay(mock);

              classUnderTest.addDocument("Document", new byte[0]);

              assertFalse(classUnderTest.removeDocument("Document"));

              verify(mock);

    }

                       

    返回值的类型,会在编译阶段检查。作为例子,下边的代码无法通过编译,因为提供的返回值与方法的返回值不匹配:

    expect(mock.voteForRemoval("Document")).andReturn("wrong type");

                       

    1.7. 不使用expect(T value)获得为对象设置的返回值,我们可以使用expectLastCall

  • 相关阅读:
    滑动窗口法学习
    209. Minimum Size Subarray Sum
    485. Max Consecutive Ones
    27. Remove Element
    167. Two Sum II
    561. Array Partition I
    344. Reverse String
    14. 最长公共前缀
    layui上传文件时出现 请求上传接口出错
    Linux-5.13将初步支持苹果M1 Soc
  • 原文地址:https://www.cnblogs.com/heidsoft/p/3832243.html
Copyright © 2011-2022 走看看