Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easymock: does the order of captures matter?

This might seem like a pretty detailed question about Easymock, but I'm having a hard time finding a support site/forum/mailing list for this library.

I'm encountering a bug when using the captures() method that seems to return the captured parameters out of order.

Here's a simplified version of what I am testing:

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);
    }

}

(Please note that this is just a simplified test case - I know that I could specify the exact value of the arguments I expect passed to my mock, but in my real case the arguments are complex objects with a handful of fields, and I want to capture the object so I can assert against just a few of those fields without re-creating the entire object in my test case).

When I run the test, it fails at:

junit.framework.ComparisonFailure: expected:<[one]> but was:<[four]>

Meaning that the parameter that EasyMock is capturing in cap1 is not the first call to the method, but the last (since the value is four). I get the same results if I reverse the captures() declarations, i.e. use cap4 with the first method call, etc.

This seems like it might be a bug within EasyMock - different parameters passed to the same method in different invocations don't seem to be capture correctly.

Is anyone else using capture() with EasyMock and having similar problems? Is there an easy workaround you know of, or a different way I can capture the parameters being passed to my mock's methods?

Update 1: fixed code sample to show I am using createMock, not createStrictMock, but I get the same error with both (although the actual value of what is captured changes).

like image 785
matt b Avatar asked Feb 19 '09 16:02

matt b


2 Answers

I've received an answer on the bug I submitted to the Easymock sourceforge site, and a developer has confirmed it is indeed a bug with this version of Easymock.

It is indeed a bug. The capture is done even if it was already done. The current workaround is to implement your own capture object and override setValue to do this:

@Override
public void setValue(T value) {
  if(!hasCaptured()) {
    super.setValue(value);
  }
}
like image 132
matt b Avatar answered Sep 27 '22 23:09

matt b


I was playing around with your test and could not solve. However I extended the Capture Class to see if the values were set in a different order (I was suspicious that EasyMock internally was using a hash with a key generated from the methods and the parameters) I was wrong the methods are set in the correct order. But there is something really weird going on.. It seems that the algorithm does some kind assigning pattern.. Well let me show the code and the strange output.... BTW the changes from mock, niceMock and strictMock didn't make anydifference..

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);
}

}

*And this is the output *

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 

Sorry I can't help you more. It might be indeed a bug in easy mock.

like image 28
pribeiro Avatar answered Sep 27 '22 21:09

pribeiro