Java 如何解决不必要的存根异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42947613/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:33:03  来源:igfitidea点击:

How to resolve Unneccessary Stubbing exception

javajunitmockito

提问by VHS

My Code is as below,

我的代码如下,

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

      }
}

I am getting below exception

我低于异常

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

Please help me how to resolve

请帮我如何解决

回答by john16384

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

The whenhere configures your mock to do something. However, you donot use this mock in any way anymore after this line (apart from doing a verify). Mockito warns you that the whenline therefore is pointless. Perhaps you made a logic error?

when这里配置您的模拟做一些事情。但是,在此行之后您不再以任何方式使用此模拟(除了执行 a verify)。Mockito 警告您该when行因此毫无意义。也许你犯了一个逻辑错误?

回答by Sumit

Replace @RunWith(MockitoJUnitRunner.class)with @RunWith(MockitoJUnitRunner.Silent.class).

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

回答by railomaya

Looking at a part of your stack trace it looks like you are stubbing the dao.doSearch()elsewhere. More like repeatedly creating the stubs of the same method.

查看堆栈跟踪的一部分,看起来您正在对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.

Consider the below Test Class for example:

例如,考虑以下测试类:

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

I would rather considering refactoring your tests to stub where necessary.

我宁愿考虑重构您的测试以在必要时存根。

回答by Greg

If you're using this style instead:

如果您改用此样式:

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

replace it with:

替换为:

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

回答by Dcortez

At first you should check your test logic. Usually there are 3 cases. First, you are mocking wrong method (you made a typo or someone changed tested code so that mocked method is no longer used). Second, your test is failing before this method is called. Third, you logic falls in wrong if/switch branch somewhere in the code so that mocked method is no called.

首先你应该检查你的测试逻辑。通常有3种情况。首先,您在嘲笑错误的方法(您打错了字或有人更改了测试代码,以便不再使用模拟方法)。其次,在调用此方法之前,您的测试失败。第三,您的逻辑在代码中的某个地方出现错误的 if/switch 分支,因此不会调用模拟方法。

If this is first case you always want to change mocked method for the one used in the code. With second and the third it depends. Usually you should just delete this mock if it has no use. But sometimes there are certain cases in parametrized tests, which should take this different path or fail earlier. Then you can split this test into two or more separate ones but that's not always good looking. 3 test methods with possibly 3 arguments providers can make you test look unreadable. In that case for JUnit 4 you silent this exception with either

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

@RunWith(MockitoJUnitRunner.Silent.class) 

annotation or if you are using rule approach

注释或者如果您使用规则方法

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

or (the same behaviour)

或(相同的行为)

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

For JUnit 5 tests you can silent this exception using annotation provided in mockito-junit-jupiterpackage.

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

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

回答by sgrillon

Silent is not a solution.You need fix your mock in your test. See official documentation here.

沉默不是解决办法。您需要在测试中修复您的模拟。请参阅此处的官方文档。

Unnecessary stubs are stubbed method calls that were never realized during test execution (see also MockitoHint), example:

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

Notice that one of the stubbed methods were never realized in the code under test, during test execution. The stray stubbing might be an oversight of the developer, the artifact of copy-paste or the effect not understanding the test/code. Either way, the developer ends up with unnecessary test code. In order to keep the codebase clean & maintainable it is necessary to remove unnecessary code. Otherwise tests are harder to read and reason about.

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

To find out more about detecting unused stubbings see MockitoHint.

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

回答by philonous

For me neither the @Rulenor the @RunWith(MockitoJUnitRunner.Silent.class)suggestions worked. It was a legacy project where we upgraded to mockito-core 2.23.0.

对我来说@Rule@RunWith(MockitoJUnitRunner.Silent.class)建议和建议都不起作用。这是一个遗留项目,我们升级到了 mockito-core 2.23.0。

We could get rid of the UnnecessaryStubbingExceptionby using:

我们可以UnnecessaryStubbingException通过使用来摆脱:

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

instead of:

代替:

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

Needless to say that you should rather look at the test code, but we needed to get the stuff compiled and the tests running first of all ;)

毋庸置疑,您应该查看测试代码,但我们需要首先编译内容并运行测试;)

回答by mohitmayank

In case of a large project, it's difficult to fix each of these exceptions. At the same time, using Silentis not advised. I have written a script to remove all the unnecessary stubbings given a list of them.

对于大型项目,很难修复这些异常中的每一个。同时,Silent不建议使用。我编写了一个脚本来删除所有不必要的存根,并给出它们的列表。

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

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

We just need to copy-paste the mvnoutput and write the list of these exceptions using regex and let the script take care of the rest.

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

回答by Jan Bodnar

I had UnnecessaryStubbingExceptionwhen I tried to use the whenmethods on a Spy object. Mockito.lenient()silenced the exception but the test results were not correct.

UnnecessaryStubbingException当我尝试when在 Spy 对象上使用这些方法时,我遇到过。 Mockito.lenient()使异常静音,但测试结果不正确。

In case of Spy objects, one has to call the methods directly.

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

回答by vikas kumar

Well, In my case Mockito error was telling me to call the actual method after the whenor wheneverstub. Since we were not invoking the conditions that we just mocked, Mockito was reporting that as unnecessary stubs or code.

好吧,就我而言,Mockito 错误告诉我在whenorwhenever存根之后调用实际方法。由于我们没有调用我们刚刚模拟的条件,Mockito 将其报告为不必要的存根或代码。

Here is what it was like when the error was coming :

这是错误发生时的情况:

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

then I just called the actual method mentioned in when statement to mock the method.

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

changes done is as below stockViewModelTest.getStockAvailability(listOf(), getStocksApiCallBack)

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

it's working now.

它现在工作。