1

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

我已经查看了 Hamcrest 匹配器,但没有找到可以对此进行测试的东西(或者误解了可用匹配器的工作方式)。有没有人有更优雅的方法?

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();
    }
}
4

4 回答 4

1

也许以下(使用 JMockit 而不是 jMock)?


import java.util.*;

import org.junit.*;
import org.junit.runner.*;

import org.hamcrest.*;
import static org.hamcrest.core.AnyOf.*;
import static org.hamcrest.core.Is.*;
import org.hamcrest.core.*;

import mockit.*;
import mockit.integration.junit4.*;

@RunWith(JMockit.class)
public class FooTest
{
   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 void setBar(final Bar bar) { this.bar = bar; }
   }

   @Test
   public void testExecuteEven(final Bar bar)
   {
      final List<String> allParameters = new ArrayList<String>();
      final List<Matcher<? extends String>> expectedParameters =
         new ArrayList<Matcher<? extends String>>();

      for (int i = 0; i < 3; i++) {
         allParameters.add("param" + i);

         if (i % 2 == 0) {
            expectedParameters.add(new IsEqual<String>("param" + i));
         }
      }

      new Expectations()
      {
         {
            bar.doIt(with(anyOf(expectedParameters))); repeats(expectedParameters.size());
         }
      };

      Foo subject = new Foo();
      subject.setBar(bar);
      subject.executeEven(allParameters);
   }

   @Test // a shorter version of the same test
   public void testExecuteEven2(final Bar bar)
   {
      final List<String> allParameters = Arrays.asList("param0", "param1", "param2");

      new Expectations()
      {
         {
            bar.doIt(with(anyOf(is("param0"), is("param2")))); repeats(2);
         }
      };

      Foo subject = new Foo();
      subject.setBar(bar);
      subject.executeEven(allParameters);
   }
}
于 2009-06-27T16:55:10.657 回答
1

我认为您当前的测试实现非常接近理想。任何进一步的压缩都有可能改变测试的语义或使读者(或两者兼而有之)模糊测试的意图。

但是,如果您正在寻找一种方法来预期特定数量的方法调用,您可以使用exactly(n).of()

mockery.checking(new Expectations() {{
  exactly(expectedParameters.length()).of(bar).doIt(with(anyOf(expectedParameters)));
}});

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

  1. 调用顺序doIt
  2. 参数列表的每个元素只传递一次

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

于 2009-07-16T20:09:56.397 回答
0

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

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);
}

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

于 2009-09-05T11:42:39.320 回答
0

值得记住的是,您不必一次创建所有期望。您可以在checking(new Expectations(){{}})块之外进行循环并在最终将其传递给嘲弄之前操纵期望列表。这有助于明确复杂的期望设置(评论也是如此!):

@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);

}

另外,我注意到您没有使用 Java 5 foreach 语句。如果您没有被 Java 4 卡住,那也可以帮助您提高清晰度。

于 2010-01-21T18:24:53.627 回答