Java 是否有一种更简洁的方法来测试对列表中每个项的模拟方法的调用

Java 是否有一种更简洁的方法来测试对列表中每个项的模拟方法的调用,java,unit-testing,jmock,jmockit,Java,Unit Testing,Jmock,Jmockit,这是我最近经常遇到的一个模式的例子。 我有一个要测试的方法,它接受一个列表,并且可以为列表中的每个项目调用一些其他方法。为了测试这一点,我定义了一个具有预期调用参数的迭代器,并在JMock预期中定义了一个循环,以检查是否针对迭代器的每个项进行了调用(请参见下面的简单示例) 我已经看过Hamcrest matchers,但还没有找到对此进行测试的东西(或者误解了可用matchers的工作原理)。有人有更优雅的方法吗 package com.hsbc.maven.versionupdater; i

这是我最近经常遇到的一个模式的例子。 我有一个要测试的方法,它接受一个列表,并且可以为列表中的每个项目调用一些其他方法。为了测试这一点,我定义了一个具有预期调用参数的迭代器,并在JMock预期中定义了一个循环,以检查是否针对迭代器的每个项进行了调用(请参见下面的简单示例)

我已经看过Hamcrest matchers,但还没有找到对此进行测试的东西(或者误解了可用matchers的工作原理)。有人有更优雅的方法吗

package com.hsbc.maven.versionupdater;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.maven.plugin.testing.AbstractMojoTestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.Sequence;
import org.jmock.internal.NamedSequence;

public class FooTest extends AbstractMojoTestCase {

    public interface Bar {
        void doIt(String arg);
    }

    public class Foo {

        private Bar bar;

        public void executeEven(final List<String> allParameters) {
            for (int i = 0; i < allParameters.size(); i++) {
                if (i % 2 == 0) {
                    bar.doIt(allParameters.get(i));
                }
            }
        }

        public Bar getBar() {
            return bar;
        }

        public void setBar(final Bar bar) {
            this.bar = bar;
        }

    }

    public void testExecuteEven() {
        Mockery mockery = new Mockery();

        final Bar bar = mockery.mock(Bar.class);
        final Sequence sequence = new NamedSequence("sequence");

        final List<String> allParameters = new ArrayList<String>();
        final List<String> expectedParameters = new ArrayList<String>();

        for (int i = 0; i < 3; i++) {
            allParameters.add("param" + i);
            if (i % 2 == 0) {
            expectedParameters.add("param" + i);
            }
        }

        final Iterator<String> iter = expectedParameters.iterator();

        mockery.checking(new Expectations() {
            {
                while (iter.hasNext()) {
                    one(bar).doIt(iter.next());
                    inSequence(sequence);
                }
            }
        });

        Foo subject = new Foo();
        subject.setBar(bar);
        subject.executeEven(allParameters);
        mockery.assertIsSatisfied();
    }
}
package com.hsbc.maven.versionUpdate;
导入java.util.ArrayList;
导入java.util.Iterator;
导入java.util.List;
导入org.apache.maven.plugin.testing.AbstractMojoTestCase;
导入org.jmock.expections;
导入org.jmock.mockry;
导入org.jmock.Sequence;
导入org.jmock.internal.NamedSequence;
公共类FooTest扩展了AbstractMojoTestCase{
公共接口栏{
void doIt(字符串arg);
}
公开课Foo{
私人酒吧;
public void executeEven(最终列出所有参数){
对于(int i=0;i
可能是以下情况(使用JMockit而不是jMock)


导入java.util.*;
导入org.junit.*;
导入org.junit.runner.*;
导入org.hamcrest.*;
导入静态org.hamcrest.core.AnyOf.*;
导入静态org.hamcrest.core.Is.*;
导入org.hamcrest.core.*;
输入mockit.*;
导入mockit.integration.junit4.*;
@RunWith(JMockit.class)
公务舱
{
公共接口栏{void doIt(字符串arg);}
公开课Foo
{
私人酒吧;
public void executeEven(最终列出所有参数)
{
对于(int i=0;i最终列表我认为您当前的测试实现非常接近理想状态。任何进一步的压缩都有可能改变测试的语义,或对读者模糊测试的意图(或两者兼而有之)

但是,如果您正在寻找一种方法来期望对某个方法进行特定数量的调用,则可以使用
精确地(n).of()

(我省略了均匀度检查,但你明白了)。这与jmockit示例中的不同答案类似。请注意,这不会测试与原始测试相同的内容。特别是,它不会检查:

  • 调用
    doIt
  • 参数列表的每个元素只传递一次

  • 例如,如果您的方法以相反的顺序迭代列表,或者如果它只调用了
    doIt
    方法
    n
    次,但每次都通过列表的第一个元素,则此测试将通过。如果您希望确保列表中的每个元素都通过,则您几乎必须对其进行迭代,设置一个单独的expecta如果您不关心调用的顺序,可以省略序列的使用(在这种情况下,您可能希望更改原始方法以接受集合而不是列表)。

    您可以简化此测试。您知道自己想要什么,因此可以更具体地描述代码:

    public void testExecuteEven() {
      final List<String> values = Arrays.asList("param0", "param1", "param2", "param3");
      Sequence evens = mockery.sequence("evens");
    
      mockery.checking(new Expectations() {{
        oneOf(bar).doIt(values.get(0)); inSequence(evens);
        oneOf(bar).doIt(values.get(2)); inSequence(evens);
      }});
    
      subject.executeEven(values);
    }
    
    public void testExecuteEven(){
    最终列表值=Arrays.asList(“param0”、“param1”、“param2”、“param3”);
    序列evens=嘲弄。序列(“evens”);
    嘲弄。检查(新期望(){{
    (bar.doIt(values.get(0));不连续(evens);
    其中一个是bar.doIt(values.get(2));inSequence(evens);
    }});
    受试者。执行者(价值观);
    }
    

    如果您使用的是JUnit 4,请不要忘记,类上的@RunWith(JMock.class)注释避免了assertessatified()调用的需要。

    值得记住的是,您不必一次创建所有期望值。您可以在
    检查(new expectations(){}}之外执行循环
    在最终将期望列表传递给模拟之前,阻止并操纵期望列表。这有助于在复杂的期望设置中保持清晰(评论也是如此!):

    @测试
    public void testExecuteEven(){
    嘲弄嘲弄=新嘲弄();
    序列evens=嘲弄。序列(“evens”);
    最终Bar=mockry.mock(Bar.class);
    列表期望值=新的ArrayList();
    最终列表allParameters=new ArrayList();
    最终列表expectedParameters=new ArrayList();
    //生成一些参数
    对于(int i=0;i<3;i++){
    所有参数。添加(“参数”+i);
    如果(i%2==0)
    
    mockery.checking(new Expectations() {{
      exactly(expectedParameters.length()).of(bar).doIt(with(anyOf(expectedParameters)));
    }});
    
    public void testExecuteEven() {
      final List<String> values = Arrays.asList("param0", "param1", "param2", "param3");
      Sequence evens = mockery.sequence("evens");
    
      mockery.checking(new Expectations() {{
        oneOf(bar).doIt(values.get(0)); inSequence(evens);
        oneOf(bar).doIt(values.get(2)); inSequence(evens);
      }});
    
      subject.executeEven(values);
    }
    
    @Test
    public void testExecuteEven() {
    
      Mockery mockery = new Mockery();
      Sequence evens = mockery.sequence("evens");
      final Bar bar = mockery.mock(Bar.class);
    
      List<Expectations> expectations = new ArrayList<Expectations>();
    
      final List<String> allParameters = new ArrayList<String>();
      final List<String> expectedParameters = new ArrayList<String>();
    
    
      // generate some parameters 
      for (int i = 0; i < 3; i++) {
          allParameters.add("param" + i);
          if (i % 2 == 0) {
          expectedParameters.add("param" + i);
          }
      }
    
      // define expectations for the expected parameters
      for (String param : expectedParameters) {
        expectations.add(new Expectations() {{ oneOf(bar).doIt(param); inSequence(evens); }});
      }
    
      // define any special expectations here
      expectations.add(new Expectations() {{ oneOf(bar).doSomethingElse() /* whatever */ }});
    
      // load the expectations into the mockery
      for (Expectations expectation : expectations) {
        mockery.checking(expectation);
      }
    
      Foo subject = new Foo();
      subject.setBar(bar);
      subject.executeEven(allParameters);
    
    }