8

私の単体テストでは、さまざまなメソッドの中で次のようなインターフェイスをモックする必要がnextItem()ありisEmpty()ます。

public interface MyQueue {
    Item nextItem();
    boolean isEmpty();
    //other methods
    ...
}

モックの私の要件は、isEmpty()最初はfalseを返す必要がありますが、nextItem()呼び出された後はisEmpty()trueを返す必要があります。したがって、私は1つのアイテムでキューをモックしています。

  1. この種のモックをmockitoで実装する最も簡単な方法は何ですか?
  2. 追加の要件を実装できますか?2回目、3回目などを呼び出すnextItem()と、特定の種類の例外が発生しますか?

PS他のメソッドが含まれているため、テスト用のインターフェイスの完全な実装を提供したくありません。その結果、コードが理解しにくく、冗長になります。

4

6 に答える 6

9

これは、thenAnswer()を使用して実現できます。これは、Mockitoのドキュメントで物議を醸している機能です。

もともとMockitoに含まれていなかったさらに別の物議を醸す機能。toReturn()またはtoThrow()のみで単純なスタブを使用することをお勧めします。これらの2つは、クリーンでシンプルなコードをテスト/テストドライブするのに十分なはずです。

thenAnswerは次のとおりです。

private boolean called = false;

when(mock.nextItem()).thenAnswer(new Answer() {
 Object answer(InvocationOnMock invocation) {   
     called = true;       
     return item;
 }
when(mock.isEmpty()).thenAnswer(new Answer() {
 Object answer(InvocationOnMock invocation) {          
     return called;
 }
});
于 2012-09-29T21:56:03.780 に答える
6

簡単な例を次に示します。

//given
MyQueue mock = mock(MyQueue.class);

given(mock.isEmpty()).willReturn(false, true);
given(mock.nextItem()).willReturn(someItem);

//when
mock.isEmpty();   //yields false
mock.nextItem();  //yields someItem
mock.isEmpty();   //yields true

//then
InOrder inOrder = inOrder(mock);
inOrder.verify(mock).isEmpty();
inOrder.verify(mock).nextItem();
inOrder.verify(mock).isEmpty();

willReturn(false, true)意味:最初の呼び出しと2番目の呼び出しで戻りfalsetrueます。InOrderオブジェクトは、呼び出し順序を確認するために使用されます。順序を変更するか、呼び出しを削除するnextItem()と、テストは失敗します。

または、次の構文を使用できます。

given(mock.isEmpty()).
        willReturn(false).
        willReturn(true).
        willThrow(SpecialException.class);

さらに強力なモックセマンティクスが必要な場合は、重砲を導入できます-カスタムアンサーコールバック:

given(mock.isEmpty()).willAnswer(new Answer<Boolean>() {
    private int counter = 0;
    @Override
    public Boolean answer(InvocationOnMock invocation) throws Throwable {
        switch(++counter) {
            case 1: return false;
            case 2: return true;
            default: throw new SpecialException();
        }
    }
});

ただし、これは保守不可能なテストコードにつながる可能性があるため、注意して使用してください。

最後に、選択したメソッドのみをモックすることで、実際のオブジェクトをスパイできます。

于 2012-09-29T21:18:58.797 に答える
2

いくつかのカスタムAnswer実装を提供できますが、そのうちの1つは他に依存します。

public class NextItemAnswer implements Answer<Item> {
   private int invocationCount = 0;
   private Item item;
   public NextItemAnswer(Item item) {
       this.item = item;
   }

   public Item answer(InvocationOnMock invocation) throws Throwable {
       invocationCount++;
       return item;
   }

   public int getInvocationCount() {
       return invocationCount;
   }
}

public class IsEmptyAnswer implements Answer<Boolean> {
   private NextItemAnswer nextItemAnswer;
   public IsEmptyAnswer(NextItemAnswer nextItemAnswer) {
       this.nextItemAnswer = nextItemAnswer;
   }
   public Boolean answer(InvocationOnMock invocation) throws Throwable {
       return nextItemAnswer.getInvocationCount() >= 0;
   }
}

そしてそれを使用します:

NextItemAnswer nextItemAnswer = new NextItemAnswer(item);
IsEmptyAnswer isEmptyAnswer = new IsEmptyAnswer(nextItemAnswer);

when(mock.isEmpty()).thenAnswer(isEmptyAnswer);
when(mock.nextItem()).thenAnswer(nextItemAnswer);

私はこのコードをテストしていないので微調整するかもしれませんが、アプローチはあなたが必要とするものでなければなりません。

于 2012-09-29T21:58:41.370 に答える
2

MyQueueの完全な実装を提供したくないと明示的に書いたことは承知していますが、正直なところ、それが私が最初に行うことです。

実際、テストを簡単にテストできるようにする目的で、適度に複雑なインターフェイス/オブジェクトの「モック」実装を定期的に提供しています。考えているのは私だけではありません。たとえば、Spring Frameworkは、複雑なオブジェクト(MockHttpServletRequest、MockHttpServletResponseなど)のモックバージョンを多数提供しています。

その場合、テストが乱雑になるのを避け、このクラスを別のパッケージで提供するか、本番コードで提供します。

MockQueueを使用すると、ここに示す他の(ただし正しい)応答よりもテストがはるかに読みやすくなります。

于 2012-10-01T12:46:10.057 に答える
1

mockitoのドキュメントで説明されている手法を使用して、同じモックメソッドへの連続した呼び出しで異なる応答をするようにmockitoに指示できます。

when(mock.isEmpty())
  .thenReturn(false)
  .thenReturn(true);

isEmpty()最初の呼び出しでのみ呼び出しがtrueに戻り、

when(mock.nextItem())
  .thenReturn(item)
  .thenThrow(new NextOnEmptyQueueException())

最初の呼び出しで何かをnextItem()返し、それ以降の呼び出しで例外をスローします。

ただし、これらのメソッドの1つの結果を、他のメソッドへの呼び出しの順序付けに依存させることができるかどうかはわかりません。それが本当に可能であるならば、私はそれがはるかに複雑であると確信しています。

于 2012-09-29T21:24:42.873 に答える
1

ユーティリティメソッドを作成して、好きな場所で使用できます。

public static boolean mockHasInvocation(Object mock, String methodName, Object... args) {
    return mockingDetails(mock).getInvocations().stream()
            .anyMatch(o -> o.getMethod().getName().equals(methodName) && Arrays.equals(o.getArguments(), args));
}

簡単な使用法:

if(mockHasInvocation(mockObject, "methodName", "argument1", "argument2")){doSomething();}

この場合、追加の変数は必要なく、より「Mockitoスタイル」です。

于 2016-10-21T14:01:01.533 に答える