Reputation: 11
In the code below I expect that given
will throw MissingMethodInvocationException
as foo()
is final.
But instead I get NullPointerException
at str.equals("String 1")
.
So, Mockito is calling real code. Why?
import static org.junit.Assert.assertEquals;
import static org.mockito.BDDMockito.given;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
class Foo {
private String str = "String 1";
public final String foo() {
if (str.equals("String 1")) {
str = "String 2";
}
return str;
}
}
@RunWith(MockitoJUnitRunner.class)
public class TestClass {
@Mock
Foo foo;
@Test
public void test() {
given(foo.foo()).willReturn("x");
assertEquals("x", foo.foo());
}
}
In the example below, I removed the if clause. Now it works as expected.
Of course I don't want to remove those lines as they are needed in the code I'm testing.
How is presence of that lines affecting Mockito's behaviour?
public final String foo() {
return str;
}
How can I make sure that Mockito will never call real code on methods even if they happen to be final?
I'd rather see MissingMethodInvocationException
.
And in this code, the test passes:
public String foo() {
if (str.equals("String 1")) {
str = "String 2";
}
return str;
}
The reason I am asking is that I have a test case and someone added final
modifier to one of the methods being tested/mocked.
Instead of seeing MissingMethodInvocationException
we saw some unrelated Exceptions thrown from 'real' code inside mocked method. We spent some time looking for the place and change that caused tests to fail.
If Mockito threw MissingMethodInvocationException
we would see the reason instantly.
Upvotes: 1
Views: 1168
Reputation: 42223
tl;dr : Mockito cannot mock final methods. And it cannot detect a final method being called either.
Longer explanation : It's one of the drawback of finals in Java. The only option you have is to use Powermock, although I would keep that use for legacy code.
The thing with Mockito is that it works by subclassing the type to mock, and the Java compiler or the JVM just won't allow anything that subclass a final class or override a final method. Mockito cannot do anything on that with our current design.
Whereas the nifty Powermock adds another step of reloading the classes in another classloader performing several modifications and especially removing the final flags in the type to mock. Though this approach has drawbacks as well : more configuration in the test, test consumes measurably more Permgen, test is slower to bootstrap.
Upvotes: 3
Reputation: 1286
You can use PowerMock
Also see Can Powermockito mock final method in non-final concrete class?
Upvotes: 0