简体   繁体   中英

Mockito doNothing on a method inside a nested mock

Suppose I have 3 classes, ClassA which contains a ClassB which contains a ClassC. I'm trying to test a method inside ClassA, that method needs to call ClassB to get the instance of ClassC and execute a VOID method inside ClassC (I know this wrong, cause ClassA should not be aware of ClassC, code smell, this is not my code and just trying to create a test of it). However when I try to skip that method call using Mockito.doNothing I get the following error:

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
E.g. 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!
 3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed

Here's the code:

ClassA:

    public class ClassA {
        private ClassB classB;

        public ClassA(ClassB classB) {
            this.classB = classB;
        }

        public void methodToTest() {
            classB.getClassC().someVoidMethod("123");
        }
    }

ClassB:

    public class ClassB {
        private ClassC classC;

        public ClassB(ClassC classC) {
            this.classC = classC;
        }

        public ClassC getClassC() {
            return classC;
        }

    }

ClassC:

    public class ClassC {
        public void someVoidMethod(String arg) {
            System.out.println(arg);
        }
    }

Test Class

    @RunWith(MockitoJUnitRunner.class)
    public class ClassATest {

        @InjectMocks
        private ClassA classA;

        @Mock(answer = Answers.RETURNS_DEEP_STUBS)
        private ClassB classB;

        @Mock
        private ClassC classC;

        @Test
        public void test() {
            Mockito.doNothing().when(classB.getClassC()).someVoidMethod("123");
        }    
    }

Once again, this code is not mine so I can't modify the way it uses the dependencies. I'm just trying to test it.

Quick Note: Mockito.when(...).thenReturn(...) works fine, but this is not my case since the method I'm trying to Mock is void.

I think this works. I am not sure why you can't use classB.getClassC() instead of referencing classC directly.

@RunWith(MockitoJUnitRunner.class)
public class ClassATest {

  @InjectMocks
  private ClassA classA;

  @Mock(answer = Answers.RETURNS_DEEP_STUBS)
  private ClassB classB;

  @Mock
  private ClassC classC;

  @Before
  public void init() {
    doReturn(classC).when(classB).getClassC();
    doNothing().when(classC).someVoidMethod("123");
  }

  @Test
  public void test() {
    classA.methodToTest();
  }
}

Your mocking syntax is improperly arranged.

Mockito.doNothing().when(classB.getClassC()).someVoidMethod("123");

When you add spaces to this statement to individual pieces it is apparent.

Mockito      response                    method                  method
Mockito      .doNothing()     .when(classB.getClassC())    .someVoidMethod("123");

When you pass a METHOD to when, it expects a thenReturn When you pass an OBJECT to when, then you can chain a void method from that.

Mockito.when(classB.getClassC()).thenReturn(classC);
Mockito.doNothing().when(classC).someVoidMethod("123");

The reason this doesn't work like you think it does is because classB.getClassC() doesn't work like a getter when passed through when() .

You're not actually passing a reference to the object to Mockito, but a method that Mockito will stub for you with thenReturn , which it complains is missing because you don't have one.

tl:dr;

Do not treat mock object methods like actual methods within mockito stubbing code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM