Easymock:捕获的顺序是否重要?

时间:2009-02-19 16:26:37

标签: java mocking easymock

这似乎是关于Easymock的一个非常详细的问题,但我很难找到这个库的支持站点/论坛/邮件列表。

我在使用captures()方法时遇到了一个错误,该方法似乎无法按顺序返回捕获的参数。

以下是我正在测试的简化版本:

public class CaptureTest extends TestCase {

    // interface we will be mocking
    interface Processor {
            void process(String x);
    }

    // class that uses the interface above which will receive the mock
    class Component {

        private Processor processor;

        private String[] s = { "one", "two", "three", "four" };

        Component(Processor processor) {
            this.processor = processor;
        }

        public void doSomething() {
            for (int i = 0; i < s.length; i++) {
                processor.process(s[i]);
            }
        }
}

    public void testCapture() {

        //create the mock, wire it up    
        Processor mockProcessor = createMock(Processor.class);
        Component component = new Component(mockProcessor);

        //we're going to call the process method four times
        //with different arguments, and we want to capture 
        //the value passed to the mock so we can assert against it later    
        Capture<String> cap1 = new Capture<String>();
        Capture<String> cap2 = new Capture<String>();
        Capture<String> cap3 = new Capture<String>();
        Capture<String> cap4 = new Capture<String>();

        mockProcessor.process(and(isA(String.class), capture(cap1)));
        mockProcessor.process(and(isA(String.class), capture(cap2)));
        mockProcessor.process(and(isA(String.class), capture(cap3)));
        mockProcessor.process(and(isA(String.class), capture(cap4)));

        replay(mockProcessor);

        component.doSomething();

        //check what values were passed to the mock
        assertEquals("one", cap1.getValue());
        assertEquals("two", cap2.getValue());
        assertEquals("three", cap3.getValue());
        assertEquals("four", cap4.getValue());

        verify(mockProcessor);
    }

}

(请注意,这只是一个简化的测试用例 - 我知道我可以指定我希望传递给我的mock的参数的确切值,但在我的实际情况中,参数是带有少量字段的复杂对象,并且我想捕获对象,这样我就可以断言这几个字段,而无需在我的测试用例中重新创建整个对象。)

当我运行测试时,它失败了:

  

junit.framework.ComparisonFailure:expected:&lt; [one]&gt;但是:&lt; [4]&gt;

意味着EasyMock在cap1中捕获的参数不是对方法的第一次调用,而是最后一次调用(因为值为four)。如果我反转captures()声明,即在第一次方法调用时使用cap4等,我会得到相同的结果。

这似乎可能是EasyMock中的一个错误 - 在不同的调用中传递给同一方法的不同参数似乎无法正确捕获。

是否有其他人使用capture()使用EasyMock并遇到类似问题?是否有一个你知道的简单解决方法,或者我可以采用不同的方式捕获传递给我的mock方法的参数?

更新1 :固定代码示例以显示我正在使用createMock,而不是createStrictMock,但我得到了同样的错误(尽管实际值是什么捕获的变化)。

6 个答案:

答案 0 :(得分:4)

我收到了an answer关于我提交给Easymock sourceforge网站的错误,并且开发人员已经确认这确实是此版本的Easymock的错误。

  

这确实是一个错误。即使已经完成捕获也已完成。该   当前的解决方法是实现自己的捕获对象并覆盖   setValue执行此操作:

@Override
public void setValue(T value) {
  if(!hasCaptured()) {
    super.setValue(value);
  }
}

答案 1 :(得分:1)

我正在玩你的测试但无法解决。 但是我扩展了Capture类以查看值是否以不同的顺序设置(我怀疑EasyMock内部使用的哈希与方法和参数生成的密钥)我错了方法设置正确的顺序。但是有一些非常奇怪的事情......似乎算法做了某种分配模式..让我展示代码和奇怪的输出......顺便说一句,来自mock,niceMock和strictMock的变化没有做出任何差异..

class MyCapture extends Capture<String> {

    private String id;

    public MyCapture(String id) {
        super();
        System.out.printf("Constructor %s expecting %s\n", id, this.getClass().getName());
        this.id = id;
    }

    private static final long serialVersionUID = 1540983654657997692L;

    @Override
    public void setValue(String value) {
        System.out.printf("setting value %s expecting %s \n", value, id);
        super.setValue(value);
    }

    @Override
    public String getValue() {
        System.out
                .printf("getting value %s expecting %s \n", super.getValue(), id);
        return super.getValue();
    }
}


public void testCapture() {

    // create the mock, wire it up
    Processor mockProcessor = createStrictMock(Processor.class);
    Component component = new Component(mockProcessor);

    // we're going to call the process method four times
    // with different arguments, and we want to capture
    // the value passed to the mock so we can assert against it later
    Capture<String> cap1 = new MyCapture("A");
    Capture<String> cap2 = new MyCapture("B");
    Capture<String> cap3 = new MyCapture("C");
    Capture<String> cap4 = new MyCapture("D");

    mockProcessor.process(and(isA(String.class), capture(cap1)));
    mockProcessor.process(and(isA(String.class), capture(cap2)));
    mockProcessor.process(and(isA(String.class), capture(cap3)));
    mockProcessor.process(and(isA(String.class), capture(cap4)));
    replay(mockProcessor);

    component.doSomething();

    // check what values were passed to the mock
    assertEquals("A", cap1.getValue());
    assertEquals("B", cap2.getValue());
    assertEquals("C", cap3.getValue());
    assertEquals("D", cap4.getValue());

    verify(mockProcessor);
}

}

*这是输出*

Constructor A expecting com.comp.core.dao.impl.CaptureTest$MyCapture
    Constructor B expecting com.comp.core.dao.impl.CaptureTest$MyCapture
    Constructor C expecting com.comp.core.dao.impl.CaptureTest$MyCapture
    Constructor D expecting com.comp.core.dao.impl.CaptureTest$MyCapture
    calling process A 
    setting value A expecting A 
    calling process B 
    setting value B expecting A <<Setting the wrong guy
    setting value B expecting A <<Setting the wrong guy
    setting value B expecting B <<Ops this is the right one..stop
    calling process C 
    setting value C expecting B <<Setting the wrong guy
    setting value C expecting B <<Setting the wrong guy
    setting value C expecting C <<Setting the wrong guy
    calling process D 
    setting value D expecting C <<Setting the wrong guy
    setting value D expecting C <<Setting the wrong guy
    setting value D expecting D <<Ops this is the right one..stop
    getting value B expecting A 

抱歉,我无法帮到你。它可能确实是一个容易模拟的错误。

答案 2 :(得分:0)

您也可以尝试使用EasyMock.createNiceMock(...)代替EasyMock.createStrictMock(...)或EasyMock.createMock(...)。

虽然,我同意它看起来更像是createMock的错误。

答案 3 :(得分:0)

我认为这是一个更适合基于状态的测试的问题。 使用JMockit,您可以像这样解决它:

import mockit.*;
import static mockit.Mockit.*;
import mockit.integration.junit3.*;

public class CaptureTest extends JMockitTestCase
{
   interface Processor { void process(String x); }

   class Component
   {
      private final Processor processor;
      private final String[] s = {"one", "two", "three", "four"};

      Component(Processor processor) { this.processor = processor; }

      public void doSomething()
      {
         for (String value : s) {
            processor.process(value);
         }
      }
   }

   @MockClass(realClass = Processor.class)
   static class MockProcessor
   {
      private final String[] expectedValues;
      private int i;

      MockProcessor(String... expectedValues) { this.expectedValues = expectedValues; }

      @Mock
      void process(String x)
      {
         assertEquals(expectedValues[i++], x);
      }
   }

   public void testCapture()
   {
      Processor mockProcessor = setUpMock(new MockProcessor("one", "two", "three", "four"));
      Component component = new Component(mockProcessor);

      component.doSomething();
   }
}

答案 4 :(得分:0)

简而言之,这对我有用:

MyClass myMock = EasyMock.createStrictMock(MyClass.class);

...

EasyMock.checkOrder(myMock, true); // before the capture and verify, not sure if it matters

...

Capture<MyArg> capturedArgs = new Capture<MyArg>();
expect(myMock.search(capture(capturedArgs))).andReturn(someRandomReturn);

PS:我使用的是EasyMock 3.0

答案 5 :(得分:-1)

不要调用 EasyMock.createStrictMock(...),而只需调用 EasyMock.createMock(...)。应该解决你的问题。