Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EasyMock: How to Verify Method Order for Set of Values Where Order of Set Does Not Matter

I have a test in which I have a set of specific values for which two different methods will execute once for each value in the set. I need to check that the two methods are called in a specific order in relation to each other, but not in relation to the order of the set of values. For example:

String[] values = { "A", "B", "C" };

for (...<loop over values...) {
    methodOne(value);
    methodTwo(value);
}

It does not matter which order values is in, but I need to verify that methodOne() and methodTwo() are called for each value in the set AND that methodOne() is always called before methodTwo().

I know that I can create a control and expect methodOne() and methodTwo() for each value, then do control.verify(), but this depends on values being in a specific order.

Is there an elegant way to do this?

Thanks

like image 341
GreenSaguaro Avatar asked Dec 03 '25 12:12

GreenSaguaro


1 Answers

You can do this using andAnswer().

Basically, inside the andAnswer() from methodOne() you set some variable to hold what the passed in value was.

Then in the andAnswer() for methodTwo() you assert that the same argument matches what you saved from your methodOne answer.

Since each call to methodOne will modify this variable it will make sure methodTwo() is always called after methodOne().

Note this solution is not thread safe

First you need something to hold the variable from the methodOne call. This can be a simple class with a single field or even an array of one element. You need this wrapper object because you need to reference it in the IAnswer which requires a final or effectively final field.

private class CurrentValue{
    private String methodOneArg;

}

Now your expectations. Here I called the class that you are testing (The System Under Test) sut:

    String[] values = new String[]{"A", "B", "C"};

    final CurrentValue currentValue = new CurrentValue();

    sut.methodOne(isA(String.class));

    expectLastCall().andAnswer(new IAnswer<Void>() {

        @Override
        public Void answer() throws Throwable {
            //save the parameter passed in to our holder object
            currentValue.methodOneArg =(String) EasyMock.getCurrentArguments()[0];
            return null;
        }

    }).times(values.length); // do this once for every element in values

    sut.methodTwo(isA(String.class));
    expectLastCall().andAnswer(new IAnswer<Void>() {

        @Override
        public Void answer() throws Throwable {
            String value =(String) EasyMock.getCurrentArguments()[0];
            //check to make sure the parameter matches the 
            //the most recent call to methodOne()

            assertEquals(currentValue.methodOneArg, value);
            return null;
        }

    }).times(values.length); // do this once for every element in values

    replay(sut);
    ... //do your test
    verify(sut);

EDIT

you are correct that if you are using EasyMock 2.4 + you can use the new Capture class to get the argument value in a cleaner way for methodOne(). However, you may still need to use the andAnswer() for methodTwo() to make sure the correct values are called in order.

Here is the same code using Capture

Capture<String> captureArg = new Capture<>();

    sut.methodOne(and(capture(captureArg), isA(String.class)));
    expectLastCall().times(values.length);

    sut.methodTwo(isA(String.class));
    expectLastCall().andAnswer(new IAnswer<Void>() {

        @Override
        public Void answer() throws Throwable {
            String value =(String) EasyMock.getCurrentArguments()[0];
            assertEquals(captureArg.getValue(), value);
            return null;
        }

    }).times(values.length);

    replay(sut);
like image 116
dkatzel Avatar answered Dec 06 '25 02:12

dkatzel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!