Java 如何解决不必要的存根异常
我的代码如下:Java 如何解决不必要的存根异常,java,junit,mockito,Java,Junit,Mockito,我的代码如下: @RunWith(MockitoJUnitRunner.class) public class MyClass { private static final String code ="Test"; @Mock private MyClassDAO dao; @InjectMocks private MyClassService Service = new MyClassServiceImpl(); @Test
@RunWith(MockitoJUnitRunner.class)
public class MyClass {
private static final String code ="Test";
@Mock
private MyClassDAO dao;
@InjectMocks
private MyClassService Service = new MyClassServiceImpl();
@Test
public void testDoSearch() throws Exception {
final String METHOD_NAME = logger.getName().concat(".testDoSearchEcRcfInspections()");
CriteriaDTO dto = new CriteriaDTO();
dto.setCode(code);
inspectionService.searchEcRcfInspections(dto);
List<SearchCriteriaDTO> summaryList = new ArrayList<SearchCriteriaDTO>();
inspectionsSummaryList.add(dto);
when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
verify(dao).doSearchInspections(dto);
}
}
请帮我解决这个问题
when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
verify(dao).doSearchInspections(dto);
此处的
when
将您的模拟配置为执行某些操作。但是,在这一行之后,您将不再以任何方式使用此模拟(除了执行验证
)。Mockito警告您,when
行因此毫无意义。也许您犯了一个逻辑错误?将@RunWith(MockitoJUnitRunner.class)
替换为@RunWith(MockitoJUnitRunner.Silent.class)
查看堆栈跟踪的一部分,看起来您正在将dao.doSearch()存根到其他地方。更像是重复创建同一方法的存根
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at service.Test.testDoSearch(Test.java:72)
Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class.
例如,考虑以下测试类:
@RunWith(MockitoJUnitRunner.class)
public class SomeTest {
@Mock
Service1 svc1Mock1;
@Mock
Service2 svc2Mock2;
@InjectMock
TestClass class;
//Assume you have many dependencies and you want to set up all the stubs
//in one place assuming that all your tests need these stubs.
//I know that any initialization code for the test can/should be in a
//@Before method. Lets assume there is another method just to create
//your stubs.
public void setUpRequiredStubs() {
when(svc1Mock1.someMethod(any(), any())).thenReturn(something));
when(svc2Mock2.someOtherMethod(any())).thenReturn(somethingElse);
}
@Test
public void methodUnderTest_StateUnderTest_ExpectedBehavior() {
// You forget that you defined the stub for svcMock1.someMethod or
//thought you could redefine it. Well you cannot. That's going to be
//a problem and would throw your UnnecessaryStubbingException.
when(svc1Mock1.someMethod(any(),any())).thenReturn(anyThing);//ERROR!
setUpRequiredStubs();
}
}
我宁愿考虑在必要时将测试重构为存根。如果您使用这种样式:
@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
替换为:
@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();
首先,您应该检查您的测试逻辑。通常有3例。首先,你们在模仿错误的方法(你们输入了一个拼写错误,或者有人修改了测试代码,使模仿的方法不再被使用)。其次,在调用此方法之前,测试失败。第三,如果/switch分支在代码中的某个地方,那么您的逻辑就会出错,这样就不会调用mock方法
如果这是第一种情况,您总是希望将模拟方法更改为代码中使用的方法。第二个和第三个视情况而定。通常,如果这个mock没有任何用处,您应该删除它。但有时在参数化测试中会出现某些情况,这些情况应该采用不同的路径,否则会更早失败。然后你可以把这个测试分成两个或多个独立的测试,但这并不总是好看的。3个测试方法(可能有3个参数)提供程序可能会使您的测试看起来不可读。在这种情况下,对于JUnit4,您可以使用
@RunWith(MockitoJUnitRunner.Silent.class)
注释,或者如果您使用的是规则方法
@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.LENIENT);
或者(同样的行为)
对于JUnit5测试,您可以使用mockito JUnit jupiter
包中提供的注释使此异常静音
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class JUnit5MockitoTest {
}
静默不是解决方案。您需要在测试中修复模拟。请参阅官方文档
不必要的存根是在测试执行期间从未实现的存根方法调用(另请参见MockitoHint),例如:
//code under test:
...
String result = translator.translate("one")
...
//test:
...
when(translator.translate("one")).thenReturn("jeden"); // <- stubbing realized during code execution
when(translator.translate("two")).thenReturn("dwa"); // <- stubbing never realized
...
//测试中的代码:
...
字符串结果=translator.translate(“一”)
...
//测试:
...
when(translator.translate(“一”))。然后返回(“杰登”);// 对我来说,@Rule
和@RunWith(MockitoJUnitRunner.Silent.class)
建议都不起作用。这是一个遗留项目,我们在其中升级到mockito core 2.23.0
我们可以使用以下方法消除不必要的stubbing异常
:
Mockito.lenient().when(mockedService.getUserById(any())).thenReturn(new User());
而不是:
when(mockedService.getUserById(any())).thenReturn(new User());
不用说,您更应该查看测试代码,但我们首先需要编译内容并运行测试;) 在大型项目中,很难修复每个异常。同时,不建议使用Silent
。我已经编写了一个脚本来删除所有不必要的存根,并给出了它们的列表
我们只需要复制粘贴mvn
输出,并使用regex编写这些异常的列表,然后让脚本处理其余的异常。当我尝试在间谍对象上使用when
方法时,我遇到了不必要的stubbingException
。
Mockito.lenient()
使异常静音,但测试结果不正确
对于间谍对象,必须直接调用这些方法
@ExtendWith(MockitoExtension.class)
@RunWith(JUnitPlatform.class)
class ArithmTest {
@Spy
private Arithm arithm;
@Test
void testAddition() {
int res = arithm.add(2, 5);
// doReturn(7).when(arithm).add(2, 5);
assertEquals(res, 7);
}
}
在我的例子中,Mockito错误告诉我在when
或where
存根之后调用实际方法。因为我们没有调用刚才模拟的条件,所以Mockito将其报告为不必要的存根或代码
下面是错误发生时的情况:
@Test
fun `should return error when item list is empty for getStockAvailability`() {
doAnswer(
Answer<Void> { invocation ->
val callback =
invocation.arguments[1] as GetStockApiCallback<StockResultViewState.Idle, StockResultViewState.Error>
callback.onApiCallError(stockResultViewStateError)
null
}
).whenever(stockViewModelTest)
.getStockAvailability(listOf(), getStocksApiCallBack)
}
它正在工作 如果在模拟时使用any(),则必须使用重新调整@RunWith(MockitoJUnitRunner.class)
@RunWith(MockitoJUnitRunner.Silent.class)
替换
@RunWith(MockitoJUnitRunner.class)
与
@RunWith(MockitoJUnitRunner.Silent.class)
或删除@RunWith(MockitoJUnitRunner.class)
或只需注释掉不需要的模拟呼叫(显示为未经授权的存根)。中已经指出了这一点,但我认为这很容易被忽视:如果您只是简单地将JUnit 4测试类转换为JUnit 5测试类,用每个
@之前的
替换现有的@之前的,您可能会遇到一个不必要的StubbingException,如果您在该设置方法中执行了一些stubing,但至少有一个测试用例没有实现
关于这一点有更多的信息,基本上在每个之前的@和之前的@之间的测试执行有细微的区别。使用@之前的,如果任何测试用例实现存根就足够了,使用@之前的每个,所有用例都必须实现存根
如果您不想将@beforeach
的设置分成许多小块(正如上面引用的评论正确指出的那样),还有另一个选项可以代替为整个测试类激活lenient模式:您只需使用lenient()
分别在@beforeach
方法中对存根进行lenient,正如其他人指出的,通常最简单的方法是删除不必要地存根方法调用的行
在我的例子中,它是在每个
之前的一个@中,并且大部分时间都是相关的。在唯一未使用该方法的测试中,我重置了模拟,例如:
myMock.reset()
希望这能帮助其他有同样问题的人
(请注意,如果在同一个mock上有多个mock调用,那么这也会很不方便,因为除了
@Test
fun `should return error when item list is empty for getStockAvailability`() {
doAnswer(
Answer<Void> { invocation ->
val callback =
invocation.arguments[1] as GetStockApiCallback<StockResultViewState.Idle, StockResultViewState.Error>
callback.onApiCallError(stockResultViewStateError)
null
}
).whenever(stockViewModelTest)
.getStockAvailability(listOf(), getStocksApiCallBack)
}
@Test
fun `should return error when item list is empty for getStockAvailability`() {
doAnswer(
Answer<Void> { invocation ->
val callback =
invocation.arguments[1] as GetStockApiCallback<StockResultViewState.Idle, StockResultViewState.Error>
callback.onApiCallError(stockResultViewStateError)
null
}
).whenever(stockViewModelTest)
.getStockAvailability(listOf(), getStocksApiCallBack)
//called the actual method here
stockViewModelTest.getStockAvailability(listOf(), getStocksApiCallBack)
}
myMock.reset()