web-dev-qa-db-ja.com

テストJava Mockitoでの動作が強化されました

Mockitoを使用して拡張されたJavaメソッドをテストしたい。問題は、拡張された機能の期待値を設定する方法がわからない場合です。次のようになります。コードは mockito googleグループ の未回答の質問から取得されました:

_import static org.mockito.Mockito.when;
import static org.testng.Assert.assertTrue;

import Java.util.ArrayList;
import Java.util.Iterator;
import Java.util.List;

import org.mockito.Mockito;
import org.testng.annotations.Test;

public class ListTest
{

  @Test
  public void test()
  {
    List<String> mockList = Mockito.mock(List.class);
    Iterator<String> mockIterator = Mockito.mock(Iterator.class);

    when(mockList.iterator()).thenReturn(mockIter);
    when(mockIter.hasNext()).thenReturn(true).thenReturn(false);
    when(mockIter.next()).thenReturn("A");

    boolean flag = false;
    for(String s : mockList) {
        flag = true;
    }
    assertTrue(flag);
  }
} 
_

Forループ内のコードは実行されません。 Java Enhanced forはリストイテレータを内部的に使用しないため、イテレータの期待値を設定することはできません。List.get()メソッドの期待値を設定することも、実装のために拡張されたものは、リストのget()メソッドも呼び出さないようです。

どんな助けでも大歓迎です。

14
HackerGil

イテレータをモックすることは私にとってはうまくいきます。以下のコードサンプルを参照してください。

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import Java.util.Collection;
import Java.util.Iterator;

import org.junit.Before;
import org.junit.Test;

public class TestMockedIterator {

    private Collection<String> fruits;
    private Iterator<String> fruitIterator;

    @SuppressWarnings("unchecked")
    @Before
    public void setUp() {
        fruitIterator = mock(Iterator.class);
        when(fruitIterator.hasNext()).thenReturn(true, true, true, false);
            when(fruitIterator.next()).thenReturn("Apple")
            .thenReturn("Banana").thenReturn("Pear");

        fruits = mock(Collection.class);
        when(fruits.iterator()).thenReturn(fruitIterator);
    }

    @Test
    public void test() {
        int iterations = 0;
        for (String fruit : fruits) {
            iterations++;
        }
        assertEquals(3, iterations);
    }
}
25
hoipolloi

私が何かを見逃していない限り、あなたはおそらくモックされた値の実際のリストを返すはずです。この場合、ジェネレーターメソッドでテスト文字列のリストを作成し、それを返すだけです。より複雑なケースでは、リストの内容をモックオブジェクトに置き換えることができます。

締めくくりとして、拡張forループを実際にモックする必要がある理由を想像することはできません。単体テストの性質は、そのレベルの検査には適していません。それでも興味深い質問です。

5
Andrew White

私は一日中これに苦労したので、何かを指摘したいだけです:

myList.forEach(...)の代わりにfor(:)構文を使用する場合は、(モックリストを設定する場所)を含める必要があります。

doCallRealMethod().when(myMockedList).forEach(anyObject());
2
OneWholeBurrito

あなたはこのようなことをしたいです。

/**
    * THe mock you want to make iterable
    */
   @Mock
   javax.inject.Instance<Integer> myMockedInstanceObject;

   /**
     * Setup the myMockedInstanceObject mock to be iterable when the business logic
     * wants to loop existing instances of the on the iterable....
     */
    private void setupTransportOrderToTransportEquipmentMapperInstancesToBeIteratble() {
        // (a) create a very real iterator object
        final Iterator<Integer> iterator = Arrays
                .asList(Integer.valueOf(1), Integer.valueOf(2)).iterator();

        // (b) make sure your mock when looped over returns a proper iterator       
        Mockito.doAnswer(new Answer<Iterator<Integer>>() {
            @Override
            public Iterator<Integer> answer(InvocationOnMock invocation)
                    throws Throwable {
                return iterator;
            }
        }).when(myMockedInstanceObject).iterator();

    }

行のコメントとjavadocは、リスト、コレクション、javax.inject.instanceなどに関係なく、反復可能なものの動作をモックする方法を十分に明確にする必要があります。

1
99Sono