Final method mocking
JavaUnit TestingTestingMockingMockitoJava Problem Overview
I need mock some class with final method using mockito. I have wrote something like this
@Test
public void test() {
B b = mock(B.class);
doReturn("bar called").when(b).bar();
assertEquals("must be \"overrided\"", "bar called", b.bar());
//bla-bla
}
class B {
public final String bar() {
return "fail";
}
}
But it fails. I tried some "hack" and it works.
@Test
public void hackTest() {
class NewB extends B {
public String barForTest() {
return bar();
}
}
NewB b = mock(NewB.class);
doReturn("bar called").when(b).barForTest();
assertEquals("must be \"overrided\"", "bar called", b.barForTest());
}
It works, but "smells".
So, Where is the right way?
Thanks.
Java Solutions
Solution 1 - Java
From the Mockito FAQ:
>What are the limitations of Mockito
>
> - Cannot mock final methods - their real behavior is executed without any exception. Mockito cannot warn you about mocking final methods so be vigilant.
Solution 2 - Java
There is no support for mocking final methods in Mockito.
As Jon Skeet commented you should be looking for a way to avoid the dependency on the final method. That said, there are some ways out through bytecode manipulation (e.g. with PowerMock)
A comparison between Mockito and PowerMock will explain things in detail.
Solution 3 - Java
You can use Powermock together with Mockito, then you do not need to subclass B.class. Just add this to the top of your test class
@RunWith(PowerMockRunner.class)
@PrepareForTest(B.class)
@PrepareForTest
instructs Powermock to instrument B.class to make the final and static methods mockable. A disadvantage of this approach is that you must use PowerMockRunner which precludes use of other test runners such as the Spring test runner.
Solution 4 - Java
Mockito 2 now supports mocking final methods but that's an "incubating" feature. It requires some steps to activate it which are described here: https://github.com/mockito/mockito/wiki/What's-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods
Solution 5 - Java
Mockito 2.x now supports final method and final class stubbing.
>Mocking of final classes and methods is an incubating, opt-in feature. This feature has to be explicitly activated by creating the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
containing a single line:
>
> mock-maker-inline
>
>After you create this file you can do:
>
>
> final class FinalClass {
> final String finalMethod() { return "something"; }
> }
>
> FinalClass concrete = new FinalClass();
>
> FinalClass mock = mock(FinalClass.class);
> given(mock.finalMethod()).willReturn("not anymore");
>
> assertThat(mock.finalMethod()).isNotEqualTo(concrete.finalMethod());
>
>
>In subsequent milestones, the team will bring a programmatic way of using this feature. We will identify and provide support for all unmockable scenarios.
Solution 6 - Java
Assuming that B class is as below:
class B {
private String barValue;
public final String bar() {
return barValue;
}
public void final setBar(String barValue) {
this.barValue = barValue;
}
}
There is a better way to do this without using PowerMockito framework. You can create a SPY for your class and can mock your final method. Below is the way to do it:
@Test
public void test() {
B b = new B();
b.setBar("bar called") //This should the expected output:final_method_bar()
B spyB = Mockito.spy(b);
assertEquals("bar called", spyB.bar());
}
Solution 7 - Java
I just did this same thing. My case was that I wanted to ensure the method didn't cause an error. But, since it's a catch/log/return method, I couldn't test for it directly without modifying the class.
I wanted to simply mock the logger I passed in. But, something about mocking the Log
interface didn't seem to work, and mocking a class like SimpleLog
didn't work because those methods are final.
I ended up creating an anonymous inner class extending SimpleLog
that overrode the base-level log(level, string, error)
method that the others all delegate to. Then the test is just waiting for a call with a level
of 5.
In general, extending a class for behavior isn't really a bad idea, and might be preferable to mocking anyway if it's not too complicated.
Solution 8 - Java
Mockito can be used to mock final classes or final methods. The problem is, this doesn't come as out of the box feature from Mockito and needs to be configured explicitely.
So, in order to do that,
Create a text file named org.mockito.plugins.MockMaker
to the project's src/test/resources/mockito-extensions
directory and add a single line of text as below
mock-maker-inline
Once done, you can use the mockito's when method to mock the behaviour like any other regular method.
See detailed examples here