在Mockito中检测到未完成的存根

运行testing时出现以下exception。 我正在使用Mockito嘲笑。 Mockito图书馆提到的提示没有帮助。

org.mockito.exceptions.misusing.UnfinishedStubbingException: Unfinished stubbing detected here: -> at com.abDomainTestFactory.myTest(DomainTestFactory.java:355) Eg 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 abDomainTestFactory.myTest(DomainTestFactory.java:276) .......... 

来自DomainTestFactory的testing代码。 当我运行下面的testing时,我看到exception

 @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中如何评估expression式和语句。

Mockito不能读取你的源代码,所以为了弄清楚你要求的是什么,它依赖于静态。 在模拟对象上调用方法时,Mockito将调用的详细信息logging在调用的内部列表中。 when方法从列表中读取这些调用的最后一个,并将这个调用logging在OngoingStubbing对象中。

该线

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

导致与Mockito的以下互动:

  • 模拟方法mainModel.getList()被调用,
  • when调用静态方法,
  • 方法thenReturn返回由when方法返回的OngoingStubbing对象上调用。

thenReturn方法然后可以指示通过OngoingStubbing方法接收到的模拟来处理对OngoingStubbing方法的任何合适的调用以返回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. SomeModel的新mock被创build(在getSomeList()
  4. 模拟方法model.getName()被调用,

在这一点上,Mockito感到困惑。 它以为你在嘲笑mainModel.getList() ,但现在你告诉它你想模拟model.getName()方法。 对于Mockito,看起来您正在执行以下操作:

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

这对于Mockito来说看起来很傻,因为它不能确定你在用mainModel.getList()做什么。

请注意,我们没有进入thenReturn方法调用,因为JVM在调用方法之前需要先评估此方法的参数。 在这种情况下,这意味着调用getSomeList()方法。

一般来说,像Mockito那样依赖静态是一个糟糕的devise决策,因为它可能导致违反最小惊讶原则的情况。 但是,Mockito的devise确实会造成明显的performance嘲讽,即使有时会引起惊讶。

最后,最近版本的Mockito在上面的错误消息中增加了一行。 这多余的行表明你可能和这个问题一样:

3:如果完成,您在“返回”指令之前将另一个模拟内容的行为存根