Menu

#10 Capturing parameters from single argument methods

EasyMock_2.5
closed
EasyMock (40)
5
2015-02-23
2008-09-28
No

First off, I'm very happy with EasyMock, it has saved me many hours of work.

Not sure if this is a bug or intended behaviour.

Having read bug 2060545
<quote>However, EasyMock behavior is perfectly normal. Captures doen't occur in
order. They occur when a call is matched.</quote>

What should happen in the case of a mock method with a single parameter that is called twice?

import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.easymock.Capture;
import org.junit.Test;

public class MultipleCaptureTest {

@Test
public void testCaptureNonStrictControl(){
    /*

     * Should multiple same-type captures be allowed when order is 
     * not enforced?
     */
    testCaptureHelper(createMock(IProcess.class));
}

@Test
public void testCaptureStrictControl(){     
    testCaptureHelper(createStrictMock(IProcess.class));
}

@SuppressWarnings("unchecked")
protected void testCaptureHelper(IProcess processMock){
    Capture<?> capture1 = new Capture();
    Capture<?> capture2 = new Capture();

    processMock.process(capture(capture1));
    processMock.process(capture(capture2));

    replay(processMock);
    processMock.process(new Object());
    processMock.process(new Object());
    verify(processMock);

    assertTrue(capture1.hasCaptured());
    assertTrue(capture2.hasCaptured());
    assertFalse(capture1.getValue() == capture2.getValue());
}

public interface IProcess {
    public void process(Object object);
}

}

Perhaps something like this would work....

public class Captures<t> implements IArgumentMatcher, Serializable {</t>

private static final long serialVersionUID = 1L;

private Capture<T> capture;

public Captures(Capture<T> captured) {
    this.capture = captured;
}

public void appendTo(StringBuffer buffer) {
    buffer.append("capture(").append(capture).append(")");
}

@SuppressWarnings("unchecked")
public boolean matches(Object actual) {
    /*

     * If the capture has already been done then this capture
     * cannot be used
     */
    if (!capture.hasCaptured()){
        capture.setValue((T) actual);
        return true;
    } else {
        return false;
    }
}

}

Although this breaks one of the unit tests...

Discussion

  • Henri Tremblay

    Henri Tremblay - 2008-09-28

    You are absolutely right. This is not the intended behavior. You fix seems to fix this (but as a side effect that I should look into) and doesn't exactly reflect what was my first intent.

    Initially, if a Capture object was used twice, the last capture should be retained. In fact, maybe a capture list can be useful... The failing test fails because of this change of behavior.

    I'll check what can be done.

     
  • Rommert de Bruijn

    I'm having the same problem. I'm calling myObject.mockMethod twice, and capture each parameter in its own Capture object. After verification, both Capture objects contain the last parameter passed to the mockObject.

    Any hope for a fix? Tnx & keep up the great work!

     
  • Henri Tremblay

    Henri Tremblay - 2009-01-23

    Hi. I didn't fix it yet but will. I'll probably add an optional capture behavior to the capture object. Should be hopefully released soon.

     
  • Rommert de Bruijn

    Hi,

    I got my code working with a dirty fix, much like Davids solution. I prevent the capture from getting a second value by overriding the setValue method:

    class MyCapture<t> extends Capture<t> {</t></t>

    /* (non-Javadoc)
    
     * @see org.easymock.Capture#setValue(java.lang.Object)
     */
    @Override
    public void setValue(final T value) {
        if (!hasCaptured()) {
            super.setValue(value);
        }
    }
    

    }

    Although this works in my particular case, I don't know what the effects will be if the order of the captures is rearranged. So a full-fledged solution is much appreciated.

     
  • martin

    martin - 2009-01-29

    To fix multiple captures I've created a MultipleCapture object (see below)

    Now you can check the n-th call

    MultipleCapture<string> capturedMessages = new MultipleCapture<string>();</string></string>

    .. method calls

    assertThat(capturedMessages.getValue(1), containsString("GROUP"));
    assertThat(capturedMessages.getValue(2), containsString("QUESTION"));

    The class:

    public class MultipleCapture<t> extends Capture<t> {</t></t>

    /**
    
     * 
     */
    private static final long serialVersionUID = 1L;
    
    private List<T> values = new ArrayList<T>();
    
    @Override
    public T getValue() {
        if (!hasCaptured()) {
            super.getValue();
        }
        return values.get(values.size() - 1);
    }
    
    /**
    
     * get the value for the n-th call
     * 
     * @param callNumber
     *            call number
     * @return
     */
    public T getValue(int callNumber) {
        return values.get(callNumber - 1);
    }
    
    /**
    
     * get all captured values
     * 
     * @return
     */
    public List<T> getValues() {
        return values;
    }
    
    @Override
    public boolean hasCaptured() {
        return values.size() > 0;
    }
    
    @Override
    public void reset() {
        values = new ArrayList<T>();
    }
    
    @Override
    public void setValue(T value) {
        values.add(value);
    }
    
    @Override
    public String toString() {
        if (!hasCaptured()) {
            return "Nothing captured yet";
        }
        return values.toString();
    }
    
     

Log in to post a comment.

MongoDB Logo MongoDB