Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/350.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java EasyMock:在集合顺序无关紧要的情况下,如何验证值集合的方法顺序_Java_Junit_Mocking_Easymock - Fatal编程技术网

Java EasyMock:在集合顺序无关紧要的情况下,如何验证值集合的方法顺序

Java EasyMock:在集合顺序无关紧要的情况下,如何验证值集合的方法顺序,java,junit,mocking,easymock,Java,Junit,Mocking,Easymock,我有一个测试,其中我有一组特定的值,对于这些值,两个不同的方法将对集合中的每个值执行一次。我需要检查这两个方法是否按照特定的顺序相互调用,而不是按照值集的顺序调用。例如: String[] values = { "A", "B", "C" }; for (...<loop over values...) { methodOne(value); methodTwo(value); } String[]值={“A”、“B”、“C”}; 对于(…您可以使用和answer()执

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

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

for (...<loop over values...) {
    methodOne(value);
    methodTwo(value);
}
String[]值={“A”、“B”、“C”};

对于(…您可以使用
和answer()
执行此操作

基本上,在
methodOne()
andAnswer()中,您设置了一些变量来保存传入的

然后在
andAnswer()
中,对于
methodTwo()
您断言相同的参数与您从methodOne答案中保存的参数相匹配

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

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

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

private class CurrentValue{
    private String methodOneArg;

}
现在是您的期望值。在这里,我调用了您正在测试的类(正在测试的系统)
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);

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

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类SetMatcher实现了IArgumentMatcher{
私人名单;
私有列表剩余值;
私有字符串currentValue=null;
私有int timesMatched=0;
私有int集匹配;
公共设置匹配器(最终列表值匹配,最终整数倍){
this.valueToMatch=新阵列列表(valueToMatch);
this.remainingValues=新的ArrayList(ValueToMatch);
this.setMatches=次;
}
公共字符串用法(){
EasyMock.reportMatcher(这个);
返回null;
}
公共无效附件(StringBuffer缓冲区){
if(this.remainingValues.size()==0){
buffer.append(“已匹配”+this.valueToMatch+“中的所有值”+this.setMatches+“时间”);
}否则{
buffer.append(“match”+this.valueToMatch+“”+this.setMatches+”每个时间);
}
}
公共布尔匹配(对象其他){
if(this.timesMatched>=this.setMatches){
this.currentValue=null;
此.timesMatched=0;
}
if(null==此.currentValue){
if(this.remainingValues.contains(其他)){
this.currentValue=(字符串)other;
此参数为1;
此.remainingValues.remove(其他);
返回true;
}
}else if(this.currentValue.equals(other)){
这个.timesMatched++;
返回true;
}
返回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);
        }
    }
}
公共类数据处理器{
私人服务一个服务一个;
私人服务2服务2;
公共数据处理器(服务一服务一、服务二服务二){
this.serviceOne=serviceOne;
this.serviceTwo=serviceTwo;
}
public void processAll(列出所有值){
列表副本=新的ArrayList(所有值);
for(字符串值:复制){
this.serviceOne.preProcessData(值);
this.service2.completTransaction(值);
}
}
}
以及测试:

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();
    }
}
公共类DataProcessorTest{
列出测试值=数组。asList(“一”、“二”、“三”、“四”、“五”);
@试验
公开无效测试(){
IMocksControl control=EasyMock.createStrictControl();
ServiceOne ServiceOne=control.createMock(ServiceOne.class);
ServiceTwo ServiceTwo=control.createMock(ServiceTwo.class);
SetMatcher matcher=新的SetMatcher(测试值,2);
对于(int i=0;i
如果出现以下情况,测试将失败:

  • ServiceOne和ServiceTwo的调用顺序错误
  • ServiceOne和ServiceTwo不是使用相同的值连续调用的
  • 使用不在指定值列表中的值调用ServiceOne或ServiceTwo
  • 调用次数超过列表中某个值的预期次数

与自定义答案相比,这里似乎更适合自定义捕获,因为需要的是传入的值。但实际上是times()将确保正确的执行次数。@kurzweil4如果您是正确的,您可以使用
Capture
类,但是如果您关心方法1总是在方法2之前调用,我认为您仍然需要
andAnswer()
用于
methodTwo()
我的想法是将它们捕获到一个有序列表中,然后在主方法完成后立即对所有值进行比较。我认为这不起作用,因为这只能证明方法的参数都是按相同的顺序调用的,而不是methodOne()是在methodTwo()之前调用的找到了使用预期EasyMock功能的解决方案。请参阅下文。感谢您的努力!@dkatzel
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();
    }
}