@RunWith(MockitoJUnitRunner.class)与MockitoAnnotations.initMocks(this)的区别

150
在编写新的jUnit4测试时,我在考虑是否要使用@RunWith(MockitoJUnitRunner.class)MockitoAnnotations.initMocks(this)
我创建了一个新的测试,向导自动为测试生成了一个Runner。MockitoJUnitRunner的Javadocs说明如下:
与JUnit 4.4及更高版本兼容,此Runner添加以下行为: - 初始化使用@Mock注释的模拟对象,因此不需要显式使用MockitoAnnotations.initMocks(Object)方法。 在每个测试方法之前初始化Mocks。 - 验证每个测试方法后的框架使用情况。
对我来说不清楚使用Runner是否比我过去使用的initMocks()方法有任何优势。

@ExtendWith(MockitoExtension.class) 完美运行! - Gaurav
2个回答

184
MockitoJUnitRunner 可以自动验证框架使用,同时提供自动的 initMocks()
自动验证框架使用非常有用。如果出现以下错误,它会给你更好的报告:
1. 调用静态方法 when,但是没有调用匹配的 thenReturnthenThrowthen 完成存根操作。(代码中的错误 1) 2. 在模拟对象上调用 verify,但是忘记了提供要验证的方法调用。(代码中的错误 2) 3. 在执行 doReturndoThrowdoAnswer 并传递模拟对象之后调用 when 方法时,忘记提供要存根的方法。(代码中的错误 3)
如果没有验证框架使用,这些错误直到下一个 Mockito 方法的调用才会被报告。这可能发生在同一个测试方法(如下面的错误 1),下一个测试方法(如下面的错误 2)或下一个测试类中。如果它们发生在您运行的最后一个测试中(如下面的错误 3),则不会被报告。
以下是每种类型错误的示例。假设这里JUnit按照下面列出的顺序运行这些测试。
@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. 
}

现在回顾我五年前写的答案,我建议尽可能使用MockitoJUnitRunner。但是,正如Tomasz Nurkiewicz正确指出的那样,如果需要其他JUnit运行器(例如Spring),则不能使用它。
我的建议现在已经改变了。自从我第一次写这篇答案以来,Mockito团队已经添加了一个新功能。它是一个JUnit规则,执行与MockitoJUnitRunner完全相同的功能。但是更好的是,它不排除其他运行器的使用。
包括:
@Rule 
public MockitoRule rule = MockitoJUnit.rule();

在你的测试类中,这将初始化模拟对象,并自动化框架验证;就像MockitoJUnitRunner一样。但是现在,您也可以使用SpringJUnit4ClassRunner或任何其他JUnitRunner。从Mockito 2.1.0开始,还有其他选项来控制报告哪些问题。


我们正在使用testng 6.8.8 + mockito 1.10.19,显然我们不能使用MockitoJUnitRunner,但验证框架仍然有效!而且它的工作方式与@David Wallace完全相同。有人能解释一下吗?这是因为我们仍然有@Before*回调和MockitoAnnotations.initMocks(this)吗? - yuranos
@yuranos87 听起来你应该提出一个新问题来问。别忘了在提问时附上你的代码 —— 如果不展示代码,那么问“为什么这段代码会做 XYZ”有点毫无意义。 - Dawood ibn Kareem
1
使用TestRunner解决方案远远优于@Rule。 - Rule
这是我最喜欢的关于Mockito的收藏答案。是时候更新它并包含JUnit5的@ExtendWith了吗? - alexandroid
2
@alexandroid 我最好的建议是你自己写一个答案,使用 @ExtendWith。这不是我熟悉的东西。Stack Overflow 的好处在于像这样的问题,你可以得到多个正确的答案。 - Dawood ibn Kareem
显示剩余2条评论

31

使用Runner可以少写一些代码(不需要@Before方法)。另一方面,有时候使用Runner是不可能的,例如当你已经在使用一个像SpringJUnit4ClassRunner这样的Runner时。

这只是个人偏好的问题。


2
除了initMocks()行,@Before方法仍然需要用于任何其他设置,对吧? - OceanBlue
2
@OceanBlue:当然,如果你的@Before方法除了initMocks()之外还包含其他内容,那么在迁移到runner后,你必须保留它。 - Tomasz Nurkiewicz
1
David Wallace的回答完全回答了我的问题,所以我接受了那个答案,但是+1指出这个运行程序不能与另一个程序(如Spring)一起使用。谢谢! - OceanBlue
2
我正在使用Spring Boot,可以说SpringJUnit4ClassRunner自动为我初始化模拟对象。但是我不知道普通的Spring是否也有这个功能。 - gustavohenke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接