EasyMock:如何验证集合顺序无关紧要的值集的方法顺序

时间:2015-03-20 22:46:11

标签: java junit mocking easymock

我有一个测试,其中我有一组特定的值,对于集合中的每个值,两个不同的方法将执行一次。我需要检查两个方法是否以特定的顺序相互调用,而不是与值集的顺序相关。例如:

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

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

values的顺序无关紧要,但我需要验证为集合中的每个值调用methodOne()methodTwo()并且methodOne()是总是在methodTwo()之前调用。

我知道我可以为每个值创建一个控件并期望methodOne()methodTwo(),然后执行control.verify(),但这取决于values按特定顺序排列

有优雅的方法吗?

由于

2 个答案:

答案 0 :(得分:0)

您可以使用andAnswer()

执行此操作

基本上,在来自andAnswer()的{​​{1}}内,您设置了一些变量来保存methodOne()中传递的内容。

然后在value andAnswer()中你断言同一个参数与你从methodOne答案中保存的那个匹配。

由于每次调用methodTwo()都会修改此变量,因此将确保始终在methodOne()之后调用methodTwo()。

注意此解决方案不是线程安全的

首先,你需要一些东西来保存methodOne调用中的变量。这可以是具有单个字段的简单类,甚至是一个元素的数组。您需要此包装器对象,因为您需要在IAnswer中引用它,这需要最终或有效的最终字段。

methodOne

现在你的期望。在这里,我调用了您正在测试的类(被测系统)private class CurrentValue{ private String methodOneArg; }

sut

修改

你是对的,如果你使用的是EasyMock 2.4 +,你可以使用新的 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); 类以更清晰的方式获取Capture的参数值。但是,您可能仍需要使用methodOne() andAnswer()来确保按顺序调用正确的值。

以下是使用Capture

的相同代码
methodTwo()

答案 1 :(得分:0)

对于那些感兴趣的人,我使用预期的EasyMock功能解决了这个问题。解决方案是创建一个自定义IArgumentMatcher来验证一组值,并强制每个值连续匹配多少次。自定义匹配器,除了使用严格的模拟外,还能完全解决原始问题。

public class SetMatcher implements IArgumentMatcher {

    private List<String> valuesToMatch;
    private List<String> remainingValues;
    private String currentValue = null;
    private int timesMatched = 0;
    private int setMatches;

    public SetMatcher(final List<String> valuesToMatch, final int times) {
        this.valuesToMatch = new ArrayList<String>(valuesToMatch);
        this.remainingValues = new ArrayList<String>(valuesToMatch);
        this.setMatches = times;
    }

    public String use() {
        EasyMock.reportMatcher(this);

        return null;
    }

    public void appendTo(StringBuffer buffer) {
        if (this.remainingValues.size() == 0) {
            buffer.append("all values in " + this.valuesToMatch + " already matched " + this.setMatches + " time(s)");
        } else {
            buffer.append("match " + this.valuesToMatch + " " + this.setMatches + " time(s) each");
        }
    }

    public boolean matches(Object other) {

        if (this.timesMatched >= this.setMatches) {
            this.currentValue = null;
            this.timesMatched = 0;
        }

        if (null == this.currentValue) {
            if (this.remainingValues.contains(other)) {
                this.currentValue = (String) other;
                this.timesMatched = 1;
                this.remainingValues.remove(other);

                return true;
            }
        } else if (this.currentValue.equals(other)) {
            this.timesMatched++;

            return true;
        }

        return false;
    }

}

正在测试的课程:

public class DataProcessor {
    private ServiceOne serviceOne;
    private ServiceTwo serviceTwo;

    public DataProcessor(ServiceOne serviceOne, ServiceTwo serviceTwo) {
        this.serviceOne = serviceOne;
        this.serviceTwo = serviceTwo;
    }

    public void processAll(List<String> allValues) {
        List<String> copy = new ArrayList<String>(allValues);
        for (String value : copy) {
            this.serviceOne.preProcessData(value);
            this.serviceTwo.completeTransaction(value);
        }
    }
}

测试:

public class DataProcessorTest {

    List<String> TEST_VALUES = Arrays.asList("One", "Two", "Three", "Four", "Five");

    @Test
    public void test() {
        IMocksControl control = EasyMock.createStrictControl();
        ServiceOne serviceOne = control.createMock(ServiceOne.class);
        ServiceTwo serviceTwo = control.createMock(ServiceTwo.class);

        SetMatcher matcher = new SetMatcher(TEST_VALUES, 2);

        for (int i = 0; i < TEST_VALUES.size(); i++) {
            serviceOne.preProcessData(matcher.use());
            serviceTwo.completeTransaction(matcher.use());
        }

        control.replay();

        DataProcessor dataProcessor = new DataProcessor(serviceOne, serviceTwo);
        dataProcessor.processAll(TEST_VALUES);

        control.verify();
    }
}

以下任何一项测试都将失败:

  • 以错误的顺序调用ServiceOne和ServiceTwo
  • 不使用相同的值连续调用ServiceOne和ServiceTwo
  • 使用不在指定值列表中的值调用ServiceOne或ServiceTwo
  • 呼叫超出了列表中值的预期次数