如何在JUnit 5中使用Mockito?

212
如何在Mockito和JUnit 5中使用注入?
在JUnit 4中,我可以使用@RunWith(MockitoJUnitRunner.class)注解。 在JUnit 5中,没有@RunWith注解。
5个回答

318

使用Mockito有不同的方法 - 我将逐一介绍它们。

手动

使用Mockito::mock手动创建模拟对象可以在任何JUnit版本(或测试框架)中运行。

基于注解

使用@Mock注解和相应的调用MockitoAnnotations::initMocks创建模拟对象可以在任何JUnit版本(或测试框架)中运行,但是Java 9可能会干扰这里,具体取决于测试代码是否最终进入模块。

Mockito扩展

JUnit 5拥有强大的扩展模型,而Mockito最近在组/工件ID org.mockitomockito-junit-jupiter下发布了一个扩展模型。

您可以通过将@ExtendWith(MockitoExtension.class)添加到测试类并使用@Mock注释模拟字段来应用该扩展模型。从MockitoExtension的JavaDoc中可以看到:

@ExtendWith(MockitoExtension.class)
public class ExampleTest {

    @Mock
    private List list;

    @Test
    public void shouldDoSomething() {
        list.add(100);
    }

}

MockitoExtension文档描述了其他实例化mock的方法,例如使用构造函数注入(如果您喜欢在测试类中使用final字段)。

无规则,无运行器

JUnit 4规则和运行器在JUnit 5中不起作用,因此MockitoRuleMockito运行器不能使用。


7
现在有一个官方的Mockito Junit5扩展,相当于MockitoJUnitRunner -> mockito-junit-jupiter。 - dan carter
1
当Mockito的官方扩展发布时,我写了一篇博客文章,详细介绍如何配置和使用它:https://solidsoft.wordpress.com/2018/03/27/convenient-mocking-in-mockito-with-junit-5-the-official-way/ - Marcin Zajączkowski
@Test注释的方法需要是公共的吗?还是“包私有”就足够了? - Geek
当使用Jupiter(通常称为“JUnit 5”)运行测试时,测试方法只需要是包可见的。 - Nicolai Parlog
1
回到这个问题 - 有没有更好的方式?有什么建议吗?还是基于个人意见? - Ermiya Eskandary
据我所知,目前没有任何方法成为了默认被接受的方式。 - Nicolai Parlog

86

使用Mockito的MockitoExtension。该扩展包含在新的artifact mockito-junit-jupiter中:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>4.5.1</version>
    <scope>test</scope>
</dependency>

它可以让你像使用JUnit 4一样编写测试:

import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;

@ExtendWith(MockitoExtension.class)
class MyTest {

    @Mock
    private Foo foo;

    @InjectMocks
    private Bar bar; // constructor injection

    ...
}

16
@ExtendWith(MockitoExtension.class) 相当于 JUnit4 的 @RunWith(MockitoJUnitRunner.class) - Sergey Nemchinov
在JUnit5中,我可以使用MockitoExtension.class对Mock对象的方法进行多次调用并获得不同的返回值吗? - Raj Hassani
@RajHassani,是的,你可以,在这里查看(https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#stubbing_consecutive_calls)。 - Jonathan

13

有不同的方法可供选择,但更清晰的方式并且也符合JUnit 5的理念是创建一个用于Mockito的org.junit.jupiter.api.extension.Extension

1) 手动创建mocks会失去额外的Mockito检查所带来的好处,以确保您正确使用该框架。

2) 在每个测试类中调用MockitoAnnotations.initMocks(this)是样板代码,我们可以避免这种情况。
在抽象类中进行此设置也不是一个好的解决方案。
它将每个测试类与基类耦合在一起。
如果您需要一个新的基本测试类,那么您就会得到一个3级类层次结构。请避免这种情况。

3) 测试规则是JUnit 4的特定内容。
甚至不要想。
文档对此非常清楚:

然而,如果您打算为JUnit 5开发新扩展,请使用JUnit Jupiter的新扩展模型,而不是JUnit 4的基于规则的模型。

4) 测试运行器真的不是扩展JUnit 5框架的方式。
JUnit 5通过提供JUnit 5扩展来简化JUnit 4的运行器,提供了一种编写测试的扩展模型。
甚至不要想。

所以更偏爱使用org.junit.jupiter.api.extension.Extension的方式。


编辑:实际上,Mockito捆绑了一个Jupiter扩展:mockito-junit-jupiter

然后,非常简单易用:

import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class FooTest {
     ...    
}

这是对Jonathan优秀答案的补充。

通过添加依赖项mockito-junit-jupiter artifact,使用@ExtendWith(MockitoExtension.class)会在执行测试时产生以下异常:

java.lang.NoSuchMethodError: org.junit.platform.commons.support.AnnotationSupport.findAnnotation(Ljava/util/Optional;Ljava/lang/Class;)Ljava/util/Optional;

问题在于mockito-junit-jupiter依赖于两个独立的库。例如对于mockito-junit-jupiter:2.19.0

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-core</artifactId>
  <version>2.19.0</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter-api</artifactId>
  <version>5.1.0</version>
  <scope>runtime</scope>
</dependency>

问题在于我使用了 junit-jupiter-api:5.0.1。因此,由于 junit-jupiter-api 在 API 方面经常更新,请确保您依赖于与 mockito-junit-jupiter 相同版本的 junit-jupiter-api

1
为什么mockito-junit-jupiter无法拉取正确版本的junit-jupiter-api - haelix
@haelix 因为此依赖项使用的版本策略依赖于Mockito库。请查看此处的版本 mockito-junit-jupiter:2.19.0。而JUnit Jupiter版本以 5 开头。mockito-junit-jupiter应该在其构件标识符中指定两个内容(Mockito版本和JUnit Jupiter版本),以使事情更清晰。例如,mockito-junit-jupiter-5.1:2.19.0 表示该库是为 JUnit Jupiter 5.1 设计的。 - davidxxx
MockitoExtensionmockito-core 版本 3.0.0 中似乎不存在。 - Thunderforge
1
@Thunderforge 这是在 mockito-junit-jupiter 中定义的。 - davidxxx

4

您需要使用新的@ExtendWith注解。

不幸的是,还没有发布扩展版。 在GitHub上,您可以看到扩展的测试版本,例如演示测试


1
链接已损坏,页面未找到。 - Derrick
该扩展已经存在,请查看其他答案。 - leokom

1
对于 Kotlin 和在注释字段时,请确保使用 lateinit 进行标记:
@ExtendWith(MockitoExtension.class)
class MyTest {
    @Mock
    lateinit var list: List

    @Test
    fun `Test should work`() {
        list.add(100)
    }
}

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