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

現在のテストの実装は、理想にかなり近いと思います。さらに圧縮すると、テストのセマンティクスが変更されるか、リーダーに対するテストの意図が不明瞭になる(またはその両方)リスクがあります。

ただし、メソッドへの特定の数の呼び出しを期待する方法を探している場合は、次を使用できますexactly(n).of()

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

(私は均一性チェックを省略しましたが、あなたはその考えを理解します)。これは、別の答えのjmockitの例に似ています。これは元のテストと同じものをテストしないことに注意してください。特に、以下をチェックしません。

  1. への呼び出しの順序doIt
  2. パラメータリストの各要素が1回だけ渡されること

たとえば、このテストは、メソッドがリストを逆の順序で繰り返した場合、またはdoItメソッドを呼び出しただけnでリストの最初の要素を毎回通過した場合に合格します。リスト内の各要素が確実に渡されるようにする場合は、それぞれに個別の期待値を設定して、それを反復処理する必要があります。呼び出しの順序を気にしない場合は、シーケンスの使用を省略できます(その場合、元のメソッドを変更して、リストではなくコレクションを受け入れることができます)。

于 2009-07-16T20:09:56.397 に答える
1

おそらく次のようになります(jMockの代わりにJMockitを使用)?


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 に答える
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 に答える