如何解决不需要的Stubbing异常

时间:2017-03-22 09:35:49

标签: java spring mockito

我的代码如下,

@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);

      }
}

我正处于异常

之下
org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
Unnecessary stubbings detected in test class: Test
Clean & maintainable test code requires zero unnecessary code.
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.
  at org.mockito.internal.exceptions.Reporter.formatUnncessaryStubbingException(Reporter.java:838)
  at org.mockito.internal.junit.UnnecessaryStubbingsReporter.validateUnusedStubs(UnnecessaryStubbingsReporter.java:34)
  at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:49)
  at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:103)
  at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
  at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

请帮我解决问题

15 个答案:

答案 0 :(得分:87)

@RunWith(MockitoJUnitRunner.class)替换为@RunWith(MockitoJUnitRunner.Silent.class)

答案 1 :(得分:42)

首先,您应该检查您的测试逻辑。通常有3个案例。首先,你是在嘲笑错误的方法(你做了一个拼写错误或有人更改了经过测试的代码,因此不再使用模拟方法)。其次,在调用此方法之前,您的测试失败。第三,你的逻辑在代码中的某个地方出现了错误的if / switch分支,因此不会调用mocked方法。

如果是第一种情况,您总是希望更改代码中使用的方法的模拟方法。它取决于第二和第三。通常你应该删除这个模拟,如果它没用。但有时在参数化测试中存在某些情况,这些情况应该采用这种不同的路径或更早地失败。然后你可以将这个测试分成两个或多个单独的测试,但这并不总是很好看。可能有3个参数提供程序的3个测试方法可以使您的测试看起来不可读。在JUnit 4的情况下,您使用

沉默此异常
@RunWith(MockitoJUnitRunner.Silent.class) 

注释或者您正在使用规则方法

@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.LENIENT);

或(相同的行为)

@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();

对于JUnit 5测试,您可以使用mockito-junit-jupiter包中提供的注释来沉默此异常。

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class JUnit5MockitoTest {
}

答案 2 :(得分:19)

 when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
 verify(dao).doSearchInspections(dto);

此处when将您的模拟配置为执行某些操作。但是,你不能在这一行之后以任何方式使用这个模拟(除了做verify之外)。 Mockito警告你when行是没有意义的。也许你犯了一个逻辑错误?

答案 3 :(得分:10)

沉默不是解决方案。您需要在测试中修复模拟。请参阅官方文档 here

不必要的存根是在测试执行期间从未实现的存根方法调用(另请参见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
 ...

请注意,在测试执行期间,被测试的代码之一从未在被测试的代码中实现。流氓存根可能是开发人员的疏忽,复制粘贴的伪影或不了解测试/代码的影响。无论哪种方式,开发人员最终都会得到不必要的测试代码。为了保持代码库的清洁和可维护性,有必要删除不必要的代码。否则,测试将更难以阅读和推理。

要了解有关检测未使用的存根的更多信息,请参阅MockitoHint。

答案 4 :(得分:8)

对我来说,@Rule@RunWith(MockitoJUnitRunner.Silent.class)的建议都不起作用。这是一个旧项目,我们升级到了模仿核心2.23.0。

我们可以使用以下方法来摆脱UnnecessaryStubbingException

Mockito.lenient().when(mockedService.getUserById(any())).thenReturn(new User());

而不是:

when(mockedService.getUserById(any())).thenReturn(new User());

不用说,您应该看一下测试代码,但是我们需要先编译好东西,然后首先运行测试;)

答案 5 :(得分:3)

查看堆栈跟踪的一部分,看起来您正在其他地方隐藏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();
    }
}

我宁愿考虑在必要时将测试重构为存根。

答案 6 :(得分:3)

正如其他人指出的那样,通常删除不必要地存根方法调用的行是最简单的。

就我而言,它位于 @BeforeEach 中,并且大部分时间都是相关的。在唯一没有使用该方法的测试中,我重置了模拟,例如:

myMock.reset()

希望这能帮助其他有同样问题的人。

(请注意,如果在同一个模拟上有多个模拟调用,这也可能很不方便,因为除了未调用的方法之外,您必须模拟所有其他方法。)

答案 7 :(得分:2)

如果你改用这种风格:

@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);

将其替换为:

@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();

答案 8 :(得分:2)

替换

@RunWith(MockitoJUnitRunner.class)

@RunWith(MockitoJUnitRunner.Silent.class)

删除@RunWith(MockitoJUnitRunner.class)

只需注释掉不需要的模拟调用(显示为未经授权的存根)。

答案 9 :(得分:1)

当我尝试在Spy对象上使用UnnecessaryStubbingException方法时,我遇到了whenMockito.lenient()消除了异常,但测试结果不正确。

对于Spy对象,必须直接调用方法。

@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);
    }
}

答案 10 :(得分:1)

好吧,在我的情况下,Mockito错误是告诉我在whenwhenever存根之后调用实际方法。由于我们没有调用我们刚才嘲笑的条件,因此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)
}

然后我只是调用了when语句中提到的实际方法来模拟该方法。

所做的更改如下 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)
}

现在正在工作。

答案 11 :(得分:0)

在大型项目中,很难修复所有这些异常。同时,不建议使用Silent。我已经编写了一个脚本,以删除其中列出的所有不必要的存根。

https://gist.github.com/cueo/da1ca49e92679ac49f808c7ef594e75b

我们只需要复制粘贴mvn输出,并使用regex编写这些异常的列表,然后让脚本处理其余的事情即可。

答案 12 :(得分:0)

this comment中已经指出了这一点,但是我认为这太容易忽略了:如果仅通过以下方式将JUnit 4测试类转换为JUnit 5测试类,则可能会遇到UnnecessaryStubbingException将现有的@Before替换为@BeforeEach,并且如果您在该设置方法中执行了某些测试案例中至少没有实现的存根操作。

This Mockito thread对此有更多信息,基本上@Before@BeforeEach之间的测试执行存在细微的差异。使用@Before,只要 any 测试用例意识到存根就足够了,而使用@BeforeEach all 用例就必须如此。

如果您不想将@BeforeEach的设置分解为许多小部分(如上面正确引用的注释所指出的那样),那么还有另一种选择,而不是为整个测试类激活宽松模式:您只能使用@BeforeEach使lenient()方法中的那些存根变宽。

答案 13 :(得分:-1)

升级到嘲笑核心:2.18.0和powermock-module-junit4:2.0.0-beta.5时,我遇到了这个问题

以上答案确实帮助了我。 除此之外,请确保您更改

旧代码:

import org.mockito.runners.MockitoJUnitRunner;

新代码:

import org.mockito.junit.MockitoJUnitRunner;

答案 14 :(得分:-3)

如果在模拟时使用any(),则必须将@RunWith(MockitoJUnitRunner.class)@RunWith(MockitoJUnitRunner.Silent.class)