テストの実行中に次の例外が発生します。私はモックにモッキートを使用しています。 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;
}
}
モッキングの内部にモッキングをネストしています。 MyMainModel
のモックが完了する前に、モックを行うgetSomeList()
を呼び出しています。これを行うと、Mockitoは気に入らなくなります。
交換
@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が何を要求しているかを把握するために、Mockitoは静的状態に大きく依存しています。モックオブジェクトのメソッドを呼び出すと、Mockitoは呼び出しの詳細を呼び出しの内部リストに記録します。 when
メソッドは、リストからこれらの呼び出しの最後を読み取り、この呼び出しを返されるOngoingStubbing
オブジェクトに記録します。
この線
Mockito.when(mainModel.getList()).thenReturn(someModelList);
mockitoとの次の相互作用を引き起こします。
mainModel.getList()
が呼び出され、when
が呼び出され、thenReturn
は、OngoingStubbing
メソッドによって返されるwhen
オブジェクトで呼び出されます。thenReturn
メソッドは、OngoingStubbing
メソッドを介して受け取ったモックに、getList
メソッドへの適切な呼び出しを処理してsomeModelList
を返すように指示できます。
実際、Mockitoはコードを認識できないため、次のようにモックを作成することもできます。
mainModel.getList();
Mockito.when((List<SomeModel>)null).thenReturn(someModelList);
特にこの場合はnull
をキャストする必要があるため、このスタイルはややわかりにくいですが、Mockitoとの相互作用の同じシーケンスを生成し、上記の行と同じ結果を達成します。
ただし、行
Mockito.when(mainModel.getList()).thenReturn(getSomeList());
mockitoとの次の相互作用を引き起こします。
mainModel.getList()
が呼び出され、when
が呼び出され、mock
の新しいSomeModel
が作成されます(getSomeList()
内)、model.getName()
が呼び出され、この時点でMockitoは混乱します。 mainModel.getList()
をモックしていると思っていましたが、今ではmodel.getName()
メソッドをモックしたいと言っています。 Mockitoにとっては、次のことをしているように見えます。
when(mainModel.getList());
// ...
when(model.getName()).thenReturn(...);
mainModel.getList()
で何をしているのかわからないため、これはMockito
にとっては馬鹿げているように見えます。
JVMはメソッドを呼び出す前にこのメソッドのパラメーターを評価する必要があるため、thenReturn
メソッド呼び出しに到達しなかったことに注意してください。この場合、これはgetSomeList()
メソッドを呼び出すことを意味します。
一般に、Mockitoのように静的状態に依存することは設計上の悪い決定です。これは、Least Astonishmentの原則に違反するケースにつながる可能性があるためです。ただし、Mockitoのデザインは、ときどき驚toにつながる場合でも、明確で表現力豊かなモックを作成します。
最後に、Mockitoの最近のバージョンでは、上記のエラーメッセージに追加の行が追加されます。この追加行は、この質問と同じ状況にある可能性があることを示しています。
3:完了したら、「thenReturn」命令の前に別のモックの動作をスタブします