@Mock、@MockBean和Mockito.mock()之间的区别

257

在创建测试和模拟依赖关系时,这三种方法有什么区别?

  1. @MockBean:

    @MockBean
    MyService myservice;
    
  2. @Mock:

    @Mock
    MyService myservice;
    
  3. Mockito.mock()

    MyService myservice = Mockito.mock(MyService.class);
    
4个回答

312

Mockito库

import org.mockito.Mock;
...
@Mock
MyService myservice;

import org.mockito.Mockito;
...
MyService myservice = Mockito.mock(MyService.class);

这些代码片段来自Mockito库,具有相同的功能。
它们允许对类或接口进行模拟,并记录和验证其行为。

使用注释的方式更短,更可取,通常更受青睐。


请注意,在测试执行期间启用Mockito注释,需要调用MockitoAnnotations.initMocks(this)静态方法。
为避免测试之间产生副作用,建议在每次测试执行之前执行此操作:

@Before 
public void initMocks() {
    MockitoAnnotations.initMocks(this);
}

通过在测试类上注释@RunWith并指定执行此任务和其他有用的事情的MockitoJUnitRunner,可以启用Mockito注释的另一种方法:

另一种启用Mockito注释的方法是通过在测试类上使用@RunWith注释并指定MockitoJUnitRunner来执行此任务以及其他有用的操作:

(Two possible translations, please choose the one that fits the context better.)
@RunWith(org.mockito.runners.MockitoJUnitRunner.class)
public MyClassTest{...}

封装Mockito库的Spring Boot库

这确实是一个Spring Boot类:

import org.springframework.boot.test.mock.mockito.MockBean;
...
@MockBean
MyService myservice;

该类包含在spring-boot-test库中。

它允许将Mockito模拟对象添加到Spring ApplicationContext中。
如果上下文中存在与声明类兼容的bean,则会将其替换为mock;
如果不存在,则会将mock作为bean添加到上下文中。

Javadoc参考:

注释可用于向Spring ApplicationContext添加模拟对象。

...

如果已在上下文中定义了与类型相同的任何现有单个bean,则将其替换为mock,如果没有定义现有bean,则将添加一个新bean。


何时使用经典/纯Mockito,何时使用Spring Boot的@MockBean

单元测试旨在独立于其他组件进行测试,并且还具有要求:执行时间尽可能快,因为这些测试可能在开发人员计算机上每天执行十几次。

因此,这里有一个简单的指南:

如果编写的测试不需要来自Spring Boot容器的任何依赖项,则应遵循经典/纯Mockito:它很快并且有利于测试组件的隔离。
如果您的测试需要依赖于Spring Boot容器并且您还想添加或模拟容器bean中的一个:使用Spring Boot的@MockBean


Spring Boot @MockBean的典型用法

当我们编写一个注释有@WebMvcTest的测试类(Web测试切片)时。

Spring Boot文档很好地总结了这一点:

通常,@WebMvcTest将仅限于单个控制器,并与@MockBean结合使用,以为所需的协作者提供模拟实现。

这是一个例子:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@WebMvcTest(FooController.class)
public class FooControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private FooService fooServiceMock;

    @Test
    public void testExample() throws Exception {
         Foo mockedFoo = new Foo("one", "two");

         Mockito.when(fooServiceMock.get(1))
                .thenReturn(mockedFoo);

         mvc.perform(get("foos/1")
            .accept(MediaType.TEXT_PLAIN))
            .andExpect(status().isOk())
            .andExpect(content().string("one two"));
    }

}

4
使用@MockBean会创建一个bean的副本并将其注入到ApplicationContext吗?还是模拟的bean的所有方法都为空?如果所有方法都为空,我能像使用@Mock一样对它们进行存根吗? 使用@MockBean会创建一个bean的副本并将其注入到ApplicationContext中。如果所有方法都为空,您可以像使用@Mock一样使用桩来为它们提供具体的行为。 - Doug
6
如所解释的,使用@MockBean将替换应用程序上下文中声明相同类型的bean。如果您的Spring配置中已经定义了该类型的bean,则将替换它。注入是在声明@MockBean的类中执行的。DI机制的工作方式是:您在DI上下文中注册一个对象,然后您可以在特定类中注入在Spring上下文中引用的对象。您不会在DI上下文中注入对象。 - davidxxx
我在关注,但是为什么服务类的覆盖率为0%? - JayC
@MockBean 的问题在于它无法与 Mockito.verify 一起使用。 - ACV

28

最后来解释一下,很容易理解。如果你查看注解的javadocs,你会看到以下区别:

@Mock: (org.mockito.Mock)

将一个字段标记为mock。

  • 允许简写的mock创建方式。
  • 最小化重复的mock创建代码。
  • 使测试类更易读。
  • 使验证错误更易于阅读,因为字段名用于标识mock。

@MockBean: (org.springframework.boot.test.mock.mockito.MockBean)

可以用作类级别的注解,也可以在@Configuration类或@RunWith SpringRunner的测试类中的字段上使用,用于向Spring ApplicationContext添加mocks。

mocks可以按类型或bean名称注册。如果上下文中定义了相同类型的现有单个bean,则该bean将被mock替换,如果未定义现有bean,则会添加新的bean。

@MockBean用于字段时,除了在应用程序上下文中注册之外,mock还将注入到字段中。

Mockito.mock()

这只是@Mock的表示形式。


7
不要忘记 @Mock 需要 MockitoRunner 或手动调用 initMocks。 - Florian Schaetz
9
@MockBean@Mock 的唯一区别是前者会将模拟对象注入到 Spring 应用上下文中,而后者不会吗? - Doug
6
你总结得很好,但需要记住MockBean是Spring Boot的一部分。 - comiventor
1
要使用@MockBean,您需要使用@RunWith(SpringRunner.class)注释该类。但是,如果想要使用@Mock,可以使用@RunWith(MockitoJUnitRunner.class)并调用initMocks(),正如@Florian-schaetz所提到的那样。 @Mock也可以与SpringRunner一起使用,但需要额外加载applicationContext,从而增加了开销。 - Jaison Varghese

4

Mocktio.mock() :-

  1. 用来创建一个类或接口的Mock对象。我们可以使用这个Mock对象来存根返回值和验证它们是否被调用。
  2. 对于将在测试用例执行期间调用其类方法的Mock对象,我们必须使用when(..)thenReturn(..)方法。

@Mock :-

  1. 这是mock()方法的简写,因此它更可取且经常被使用。
  2. mock()@Mock在功能上是等效的。
  3. 易于在Mock失败时通过字段名称在错误消息中找到问题所在。

为了在测试执行期间启用Mockito注释,我们需要调用MockitoAnnotations.openMocks(this)方法,而不是已弃用的MockitoAnnotations.initMocks(this)方法。为避免副作用,建议在测试用例执行之前调用此方法。

另一种启用Mockito注释的方式是通过使用@RunWith注释测试类,并指定执行此任务以及其他有用任务的MockitoJUnitRunner。

@MockBean

它用于将Mock对象添加到Spring应用程序上下文中。该Mock对象将替换应用程序上下文中相同类型的现有bean。如果没有可用的bean,则将添加新bean。这对于集成测试用例非常有用。

当我们编写不需要从Spring Boot容器中获取任何依赖项的测试用例时,使用经典/普通的Mockito,它快速且支持被测试组件的隔离。

如果我们的测试用例需要依赖于Spring Boot容器,并且想要添加或Mock容器中的一个Bean,则使用@MockBean


这是我认为更简单清晰的解释。 - Marco Sulla

0
简言之,@MockBean注解的作用是:该模拟将替代应用程序上下文中的任何现有相同类型的bean,如果没有定义相同类型的bean,则会包含一个新的。在集成测试期间,当需要模拟特定的bean(如外部服务)时,此注解非常有价值。

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