• 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

  • 相关阅读:
    Codeforces 834D The Bakery
    hdu 1394 Minimum Inversion Number
    Codeforces 837E Vasya's Function
    Codeforces 837D Round Subset
    Codeforces 825E Minimal Labels
    Codeforces 437D The Child and Zoo
    Codeforces 822D My pretty girl Noora
    Codeforces 799D Field expansion
    Codeforces 438D The Child and Sequence
    Codeforces Round #427 (Div. 2) Problem D Palindromic characteristics (Codeforces 835D)
  • 原文地址:https://www.cnblogs.com/heidsoft/p/3832243.html
Copyright © 2020-2023  润新知