ChatGPT解决这个技术问题 Extra ChatGPT

@RunWith(MockitoJUnitRunner.class) vs MockitoAnnotations.initMocks(this)

While writing a new jUnit4 test, I'm wondering whether to use @RunWith(MockitoJUnitRunner.class) or MockitoAnnotations.initMocks(this).

I created a new test & the wizard automatically generated a test with the Runner. Javadocs for MockitoJUnitRunner state the following:

Compatible with JUnit 4.4 and higher, this runner adds following behavior: Initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary. Mocks are initialized before each test method. Validates framework usage after each test method.

It's not clear to me whether using the Runner has any advantage over the initMocks() method I have been using in past.

@ExtendWith(MockitoExtension.class) works flawlessly!

D
Dawood ibn Kareem

MockitoJUnitRunner gives you automatic validation of framework usage, as well as an automatic initMocks().

The automatic validation of framework usage is actually worth having. It gives you better reporting if you make one of these mistakes.

You call the static when method, but don't complete the stubbing with a matching thenReturn, thenThrow or then. (Error 1 in the code below)

You call verify on a mock, but forget to provide the method call that you are trying to verify. (Error 2 in the code below)

You call the when method after doReturn, doThrow or doAnswer and pass a mock, but forget to provide the method that you are trying to stub. (Error 3 in the code below)

If you don't have validation of framework usage, these mistakes are not reported until the following call to a Mockito method. This might be

in the same test method (like error 1 below),

in the next test method (like error 2 below),

in the next test class.

If they occur in the last test that you run (like error 3 below), they won't be reported at all.

Here's how each of those types of errors might look. Assume here that JUnit runs these tests in the order they're listed here.

@Test
public void test1() {

    // ERROR 1
    // This compiles and runs, but it's an invalid use of the framework because 
    // Mockito is still waiting to find out what it should do when myMethod is called.
    // But Mockito can't report it yet, because the call to thenReturn might 
    // be yet to happen.
    when(myMock.method1());

    doSomeTestingStuff();

    // ERROR 1 is reported on the following line, even though it's not the line with
    // the error.
    verify(myMock).method2();

}

@Test
public void test2() {

    doSomeTestingStuff();

    // ERROR 2
    // This compiles and runs, but it's an invalid use of the framework because
    // Mockito doesn't know what method call to verify.  But Mockito can't report 
    // it yet, because the call to the method that's being verified might 
    // be yet to happen.
    verify(myMock);
}

@Test
public void test3() {

    // ERROR 2 is reported on the following line, even though it's not even in 
    // the same test as the error.
    doReturn("Hello").when(myMock).method1();


    // ERROR 3
    // This compiles and runs, but it's an invalid use of the framework because
    // Mockito doesn't know what method call is being stubbed.  But Mockito can't 
    // report it yet, because the call to the method that's being stubbed might 
    // be yet to happen.

    doReturn("World").when(myMock);

    doSomeTestingStuff(); 

    //  ERROR 3 is never reported, because there are no more Mockito calls. 
}

Now when I first wrote this answer more than five years ago, I wrote

So I would recommend the use of the MockitoJUnitRunner wherever possible. However, as Tomasz Nurkiewicz has correctly pointed out, you can't use it if you need another JUnit runner, such as the Spring one.

My recommendation has now changed. The Mockito team have added a new feature since I first wrote this answer. It's a JUnit rule, which performs exactly the same function as the MockitoJUnitRunner. But it's better, because it doesn't preclude the use of other runners.

Include

@Rule 
public MockitoRule rule = MockitoJUnit.rule();

in your test class. This initialises the mocks, and automates the framework validation; just like MockitoJUnitRunner does. But now, you can use SpringJUnit4ClassRunner or any other JUnitRunner as well. From Mockito 2.1.0 onwards, there are additional options that control exactly what kind of problems get reported.


i definitely can't say they're the same. in one test case, the junit runner setup fails for me and doesn't inject my mocks properly unless i do the initMocks setup
We are using testng 6.8.8 + mockito 1.10.19 and, obviously we can't use MockitoJUnitRunner, but the validation framework still works! And it works exactly as @David Wallace. Can someone explain? Is this because we still have @Before* callbacks and MockitoAnnotations.initMocks(this)?
@yuranos87 Sounds like something you should ask as a new question. Don't forget to include your code when you do - it's a bit pointless asking "why does this code do XYZ" if you don't show the code.
using the TestRunner solution outperforms the @Rule by far
@alexandroid My best suggestion is for you to write your own answer, using @ExtendWith. It's not really something I know about. The great thing about Stack Overflow is that on a question like this, you can end up with multiple correct answers.
T
Tomasz Nurkiewicz

Using runner lets you save a little bit of coding (no need for @Before method). On the other hand using a runner is sometimes not possible, i.e. when you are already using one, like SpringJUnit4ClassRunner.

That's it. It is just a matter of preference.


Other than the initMocks() line, the @Before method would still be needed for any other setup, right?
@OceanBlue: Of course if your @Before method contained anything except initMocks() than you have to preserve it after migrating to runner.
David Wallace's answer about the validation of framework answers my question fully so I've accepted that one, but +1 for pointing out that this runner cannot be used with another one, like the Spring one. Thanks!
I'm using Spring Boot and I can say that SpringJUnit4ClassRunner automatically initializes mocks for me. I don't know about plain Spring, though.