1

私はJavaクラスを持っています:

  import java.util.List;
  public class Service
  {
     public List<Object> someMethod(final List<Object> list) {
        return null;
     }
  }

そして、カスタム マッチャーを定義した Spock テスト:

import org.mockito.ArgumentMatcher import spock.lang.Specification

    import static org.mockito.Mockito.*

    class InstantBookingInitialDecisionTest extends Specification {

        def mock = mock(Service.class)

        def setup() {
            when(mock.someMethod(argThat(hasSize(2)))).thenReturn([])
            when(mock.someMethod(argThat(hasSize(3)))).thenReturn([])
        }

        def 'Minimum hunger requirements do not apply to schedulable pros'() {
            when:
            'something'
            then:
            'something else'
        }

        // Damn, there's a Hamcrest matcher for this, but it's not in the jar that the javadocs say it is, so making my own
        static def hasSize(size) {
            new ArgumentMatcher<List>() {
                @Override
                boolean matches(Object o) {
                    List list = (List) o
                    return list.size() == size
                }
            }
        }
    }

このテストでは、次のエラーが表示されます。

java.lang.NullPointerException: Cannot invoke method size() on null object

のいずれかを削除しても、whenエラーは発生しません。気に入らないのは、テストのスタブ部分と、カスタム マッチャーを 2 回使用したことです。

ノート:

  1. 特定のサイズのmockito anyListとMockitoのドキュメントのように、リストサイズごとに個別のクラスを宣言しようとしました。同じエラーが発生します。
  2. このような Hamcrest マッチャーを使用しようとしましたが、1.3 の Javadoc に Matchers.hasSize() メソッドがリストされているにもかかわらず、インポートした 1.3 jar にマッチャーが含まれていません。(ただし、依存関係が解決されたとしても、問題を理解したいと思います。)

Spock Mocks の代わりに Mockito を使用している理由を聞かないでください - 私には理由があります。;)

ありがとうございました

4

1 に答える 1

2

根本的な原因は、カスタム マッチャーが、Matcher の一般的な規約に準拠しない例外をスローする可能性があることです。whenMockito の内部構造が原因で、それに遭遇しています。

Matcher のコントラクトは、任意のmatches(Object)オブジェクトを受け入れてtrue または false を返すことができると述べています。これは、Matcher のすべての実装で、渡されたオブジェクトの型、またはオブジェクトが null でないかどうかについて仮定を行うべきではないことを意味します。結局のところ、完全に有効で便利なマッチャーです。null またはリスト以外の引数に対してMatcher を返したい場合は、手動で確認するか、そのような場合にHamcrestが返されるように拡張する必要があります。そうしないと、キャッチされない ClassCastException または NullPointerException が発生する危険があります。それがここでの唯一の本当の問題であり、それを修正することで問題が解決します。isNull()falseTypeSafeMatcher<List>BaseMatcherfalse


ただし、これはMockito の構文を説明する良い機会です。最初の行には問題がないのに、なぜ 2 番目の行は失敗するのでしょうか? 答えは、2 行目が実行されるときです。

when(mock.someMethod(argThat(hasSize(3)))).thenReturn([])

...その後、Java は への呼び出しを評価するためwhen、次のように実行されます。

     mock.someMethod(argThat(hasSize(3)))

...そして、最後に呼び出されたメソッドとしてwhen検出someMethodし、そのスタブ化を開始できます。他のすべての Mockito マッチャーと同様に、呼び出しはargThat返されますnull(その副作用をスタックに保持しておくと、Mockito はwhen後で Java 呼び出しを分析できます)、someMethod戻り値が必要であり、Mockito は呼び出しようとしていることを検出できませんwhen。これは、既存のスタブをチェックすることを意味するため、nullfromargThatを Matcher にパイプして、最初のスタブを適用する必要があるかどうかを確認しますNullPointerException。(argThatの戻り値と Mockito の評価順序については、別の SO answerでもう少し説明します。)

とにかくMatcherを修正したいでしょうが、次のように2行目を言い換えることもできます:

doReturn([]).when(mock).someMethod(argThat(hasSize(3)))

... whenbeforeへの呼び出しsomeMethodは、Mockito が一時的にスタブを武装解除できることを意味するためです。ただし、最初の行が例外をスローしたり、実際の実装を呼び出したりしない限り、構文を のままにしても害はなくwhen、Mockito は検証を適切に処理します。

于 2016-03-03T00:43:00.380 に答える