8
votes

My scenario is pretty simple. Trying to use partial mocks, according to last answer on this and the documentation of Mockito itself. My test is:

@Test
public void test() {
    ClassUnderTest realObject = new ClassUnderTest();
    ClassUnderTest spy = spy(realObject);
    when(spy.methodB()).thenThrow(new Exception("Testing"));

    spy.methodA();
}

and the class under test is:

import org.apache.commons.lang3.NotImplementedException;

public class ClassUnderTest {

    int methodB(){
        throw new NotImplementedException("Not implemented");
    }

    public int methodA(){
        methodB();
        return 0;
    }

}

I would expect that my spying object would call the method B raising the "Testing" exception while actually the real method is called throwing the "Not implemented" exception. It behaves like I don't have a partial mock behavior in place

Why is that? What am I missing?

EDIT: As RPresle suggested, I tried using the syntax

doThrow(new Exception("Testing")).when(spy.methodB());

However, I get an UnfinishedStubbingException:

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
 -> at SimpleTest.test(SimpleTest.java:15)

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

at SimpleTest.test(SimpleTest.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
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)

Could anyone provide more guidance?

1
You did .when(spy.methodB()); whereas RPresle wrote .when(spy).methodB();QBrute
You are right. Thank you very much. Do you think I should remove my edit, or could my "typo" be useful for future readers?Pantelis Natsiavas

1 Answers

13
votes

Refering to this tutorial on Mockito, you can see that Mockito do call the original method.

There is only one caveat to this syntax. The real rule.createFileTemplate() method will be called once. This can have a lot of side effects and might even fail throwing an exception (very often a NPE). To solve this, you can (should?) use this alternative Mockito syntax:

doReturn(mockFileTemplate).when(rule).createFileTemplate();

In order to avoid this you should use the other syntax of Mockito doReturn().when()

doThrow(new Exception("Testing")).when(spy).methodB();

Hope this help.

EDIT :

Please notice that the structure is a little bit different than the when().thenReturn().

doThrow(new Exception("Testing"))
    .when(spy)
    .methodB();