How to resolve Unneccessary Stubbing exception

前端 未结 13 1359
无人共我
无人共我 2020-12-24 03:57

My Code is as below,

@RunWith(MockitoJUnitRunner.class)
public class MyClass {

    private static final String code =\"Test\";

    @Mock
     private MyCl         


        
相关标签:
13条回答
  • 2020-12-24 04:43

    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.

    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

    @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-jupiter package.

    @ExtendWith(MockitoExtension.class)
    @MockitoSettings(strictness = Strictness.LENIENT)
    class JUnit5MockitoTest {
    }
    
    0 讨论(0)
  • 2020-12-24 04:47

    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.

    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.

    0 讨论(0)
  • 2020-12-24 04:51

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

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

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

    0 讨论(0)
  • 2020-12-24 04:53

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

    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.

    changes done is as below 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.

    0 讨论(0)
  • 2020-12-24 04:53

    This was already pointed out in this comment, but I think that's too easy to overlook: You may run into an UnnecessaryStubbingException if you simply convert a JUnit 4 test class to a JUnit 5 test class by replacing an existing @Before with @BeforeEach, and if you perform some stubbing in that setup method that is not realized by at least one of the test cases.

    This Mockito thread has more information on that, basically there is a subtle difference in the test execution between @Before and @BeforeEach. With @Before, it was sufficient if any test case realized the stubbings, with @BeforeEach, all cases would have to.

    If you don't want to break up the setup of @BeforeEach into many small bits (as the comment cited above rightly points out), there's another option still instead of activating the lenient mode for the whole test class: you can merely make those stubbings in the @BeforeEach method lenient individually using lenient().

    0 讨论(0)
  • 2020-12-24 05:01

    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:

    //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.

    0 讨论(0)
提交回复
热议问题