Mockitoで検出された未完成のスタブ


151

テストの実行中に次の例外が発生します。私はモッキングにモッキートを使用しています。Mockitoライブラリで言及されているヒントは役に立ちません。

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
    -> at com.a.b.DomainTestFactory.myTest(DomainTestFactory.java:355)

    E.g. thenReturn() may be missing.
    Examples of correct stubbing:
        when(mock.isOk()).thenReturn(true);
        when(mock.isOk()).thenThrow(exception);
        doThrow(exception).when(mock).someVoidMethod();
    Hints:
     1. missing thenReturn()
     2. you are trying to stub a final method, you naughty developer!

        at a.b.DomainTestFactory.myTest(DomainTestFactory.java:276)
        ..........

からのテストコードDomainTestFactory。次のテストを実行すると、例外が表示されます。

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    Mockito.when(mainModel.getList()).thenReturn(getSomeList()); // Line 355
}

private List<SomeModel> getSomeList() {
    SomeModel model = Mockito.mock(SomeModel.class);
    Mockito.when(model.getName()).thenReturn("SomeName"); // Line 276
    Mockito.when(model.getAddress()).thenReturn("Address");
    return Arrays.asList(model);
}

public class SomeModel extends SomeInputModel{
    protected String address;
    protected List<SomeClass> properties;

    public SomeModel() {
        this.Properties = new java.util.ArrayList<SomeClass>(); 
    }

    public String getAddress() {
        return this.address;
    }

}

public class SomeInputModel{

    public NetworkInputModel() {
        this.Properties = new java.util.ArrayList<SomeClass>(); 
    }

    protected String Name;
    protected List<SomeClass> properties;

    public String getName() {
        return this.Name;
    }

    public void setName(String value) {
        this.Name = value;
    }
}

こんにちはMureinik、投稿を行番号で更新しました
Royal Rose

回答:


371

あなたはモックの中にモックを入れ子にしています。getSomeList()のモックが完了する前に、モックを行うを呼び出していますMyMainModel。あなたがこれをするとき、モッキートはそれを好きではありません。

交換する

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    Mockito.when(mainModel.getList()).thenReturn(getSomeList()); --> Line 355
}

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    List<SomeModel> someModelList = getSomeList();
    Mockito.when(mainModel.getList()).thenReturn(someModelList);
}

これが問題を引き起こす理由を理解するには、Mockitoがどのように機能するかについて少し理解し、Javaで評価される式とステートメントの順序に注意する必要があります。

Mockitoはソースコードを読み取ることができないため、何を要求しているのかを理解するために、静的な状態に大きく依存しています。モックオブジェクトのメソッドを呼び出すと、Mockitoは呼び出しの内部リストに呼び出しの詳細を記録します。このwhenメソッドは、リストからこれらの呼び出しの最後を読み取り、OngoingStubbing返されたオブジェクトにこの呼び出しを記録します。

この線

Mockito.when(mainModel.getList()).thenReturn(someModelList);

Mockitoとの以下の相互作用を引き起こします:

  • モックメソッドmainModel.getList()が呼び出され、
  • 静的メソッドwhenが呼び出され、
  • メソッドthenReturnOngoingStubbingwhenメソッドによって返されたオブジェクトで呼び出されます。

次に、thenReturnメソッドは、OngoingStubbingメソッドを介して受け取ったモックに、メソッドへの適切な呼び出しを処理getListして返すように指示できsomeModelListます。

実際、Mockitoはコードを認識できないため、次のようにモッキングを作成することもできます。

mainModel.getList();
Mockito.when((List<SomeModel>)null).thenReturn(someModelList);

特にこの場合はnullキャストする必要があるため、このスタイルは読みにくくなりますが、Mockitoと同じ一連の対話を生成し、上記の行と同じ結果を実現します。

ただし、行

Mockito.when(mainModel.getList()).thenReturn(getSomeList());

Mockitoとの以下の相互作用を引き起こします:

  1. モックメソッドmainModel.getList()が呼び出され、
  2. 静的メソッドwhenが呼び出され、
  3. 新しいmockSomeModel作成される(内側getSomeList())、
  4. モックメソッドmodel.getName()が呼び出され、

この時点で、Mockitoは混乱します。あなたはあざけると思っていましたがmainModel.getList()、今はmodel.getName()メソッドをあざけたいと言っています。Mockitoにとって、あなたは次のことをしているように見えます。

when(mainModel.getList());
// ...
when(model.getName()).thenReturn(...);

Mockito何をしているのかわからないので、これはばかげて見えますmainModel.getList()

thenReturnJVMがメソッドを呼び出す前に、このメソッドのパラメーターを評価する必要があるため、メソッド呼び出しに到達しなかったことに注意してください。この場合、これはgetSomeList()メソッドを呼び出すことを意味します。

一般的に、Mockitoのように静的状態に依存することは設計上の決定としては好ましくありません。これは、最小驚きの原則に違反する場合があるためです。しかし、Mockitoのデザインは、時には驚かされる場合でも、明確で表現力豊かなモックを作成します。

最後に、Mockitoの最近のバージョンでは、上記のエラーメッセージに行が追加されています。この余分な行は、この質問と同じ状況にある可能性があることを示しています。

3:完了した場合、「thenReturn」命令の前に別のモックの動作をスタブしている


この事実について何か説明はありますか?ソリューションは機能します。そして、「インプレース」のモック作成が機能しない理由がわかりません。モックを作成し、作成したモックを他のモックを参照して渡すと、機能します。
Capacytron 2014年

1
すばらしい答え、SOが大好きです!これを自分で見つけるには年齢を
要した

4
正解ルーク!簡単な言葉で非常に詳細な説明。ありがとうございました。
TomaszKalkosiński15年

1
驚くばかり。おもしろいのは、直接メソッドを呼び出してゆっくりデバッグすると、動作することです。CGLIB $ BOUNDの属性は値trueを取得しますが、どういうわけかそれは少し時間がかかります。直接メソッド呼び出しを使用してトレーニングの前に停止すると(...の場合)、値が最初にfalseになり、後でtrueになることがわかります。falseでトレーニングが開始されると、この例外が発生します。
Michael Hegner 2017年

あなたは私の日を作りました!これは、多くの時間を無駄にする種類のエラーです!最初はコトリンに関連したものだと思った
ブロンクス

1
org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
E.g. thenReturn() may be missing.

voidメソッドのモックについては、以下を試してください。

//Kotlin Syntax

 Mockito.`when`(voidMethodCall())
           .then {
                Unit //Do Nothing
            }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.