1

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.

Karol123
  • 11
  • 2

2 Answers2

3

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.

bric3
  • 40,072
  • 9
  • 91
  • 111
  • Ok, I can see that Mockito does not work on final methods. But I fail to see why it calls real code in some cases (`foo` method with `if`) and sometimes it throws `MissingMethodInvocationException` (`foo` method without `if`). The fact that method under test was `final` was a mistake. I find it very troubling that Mockito throws an exception in some cases a final method is mocked and in other cases executes real code silently. I'd rather see an Exception every time a final method is mocked. I'd never want real code called on mock. – Karol123 Jul 04 '14 at 11:49
  • You or Mockito just can't block real code from being executed when the method is final, the JVM will always call the final method. i.e `foo.foo()` will always call the real method as Mockito simply cannot stub `foo`. – bric3 Jul 04 '14 at 11:56
0

You can use PowerMock

Also see Can Powermockito mock final method in non-final concrete class?

Community
  • 1
  • 1
rob
  • 1,286
  • 11
  • 12
  • See my edit. We didn't want this method to be final. It was a mistake. But Mockito did not help us finding it by throwing `MissingMethodInvocationException` on an attempt to mock final method. Instead it executed real code. That's the problem. – Karol123 Jul 04 '14 at 10:26
  • So want you really want is to list all mocked methods that are called in your test that are final ? – rob Jul 04 '14 at 10:32
  • 1
    What I want is to avoid calling real code on mocks in case someone adds final modifier to a method by mistake. I would never assume a mock would call real code in any circumstances. I was wrong. Now I want to know why is Mockito doing this. – Karol123 Jul 04 '14 at 10:37
  • When using the @Mock annotation, the private fields of the mocked class are not initialized. Therefore 'str' is null and never set to "String 1", which explains the NPE – rob Jul 04 '14 at 10:55
  • Yes, I got it. But what explains calling the actual method `foo()`? That's what I am asking about. There is a call to `given` to mock a final method. Why is Mockito executing real code instead of complaining about final method being mocked? – Karol123 Jul 04 '14 at 10:56
  • You are not getting a MissingMethodInvocationException because the method does exist. Simply the given - willReturn statement has no effect because the method is final – rob Jul 04 '14 at 10:59
  • So why I do get `MissingMethodInvocationException` when the method is final but without if-clause? The method do exist and I do get `MissingMethodInvocationException`. If I add the if clause, Mockito is executing it. I don't want this code to be executed. It is a mock after all. – Karol123 Jul 04 '14 at 11:03