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


147

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

  1. @MockBean:

    @MockBean
    MyService myservice;
  2. @嘲笑:

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

    MyService myservice = Mockito.mock(MyService.class);

Answers:


197

普通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);
}

启用Mockito注释的另一种方法是@RunWith通过指定MockitoJUnitRunner执行此任务的,以及其他有用的东西来注释测试类:

@RunWith(org.mockito.runners.MockitoJUnitRunner.class)
public MyClassTest{...}

Spring Boot库包装Mockito库

这确实是Spring Boot类

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

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

它允许在Spring中添加Mockito模拟ApplicationContext
如果上下文中存在与声明的类兼容的bean,则将其替换为模拟。
如果不是这种情况,则添加模拟的背景下,作为一个bean。

Javadoc参考:

可用于将模拟添加到Spring ApplicationContext的注释。

...

如果在上下文中定义的任何现有的相同类型的单个Bean将被模拟代替,如果没有定义现有的Bean,则将添加一个新的。


什么时候使用经典/普通Mockito以及何时@MockBean从Spring Boot 使用?

单元测试旨在与其他组件隔离地测试组件,并且单元测试也有一个要求:执行时间要尽可能快,因为这些测试每天可能在开发人员计算机上执行数十次。

因此,这是一个简单的准则:

当您编写不需要从Spring Boot容器中获取任何依赖项的测试时,遵循经典/普通Mockito的方法是:它快速且有利于隔离测试的组件。
如果您的测试需要依赖于Spring Boot容器,并且您还想添加或模拟其中一个容器bean:@MockBean从Spring Boot开始就是这样。


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的所有方法都设置为null?如果所有方法都为空,是否可以像使用@Mock一样对它们进行存根?
Doug

6
如前所述,@MockBean如果您的Spring配置中已经定义了声明相同类型的bean ,则using 将在应用程序上下文中替换bean。注入是在您声明的类中执行 @MockBean.的。DI机制以这种方式工作:在DI上下文中注册一个对象,然后可以将Spring上下文中引用的对象注入到特定的类中。您不会在DI上下文中注入对象。
davidxxx

13

最后,它很容易解释。如果仅查看注释的javadocs,您将看到不同之处:

@模拟:(org.mockito.Mock

将字段标记为模拟。

  • 允许速记模拟创建。
  • 最小化重复的模拟创建代码。
  • 使测试类更具可读性。
  • 因为字段名称用于标识模拟,所以使验证错误更易于阅读。

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

可用于将模拟添加到Spring ApplicationContext的注释。可用作类级别的注释或在@Configuration类或测试类中的字段上@RunWith用作SpringRunner的的字段。

可以通过类型或bean名称来注册模拟。在上下文中定义的任何现有的相同类型的单个Bean将由模拟代替,如果未定义现有Bean,则将添加一个新的。

@MockBean在字段上使用以及在应用程序上下文中注册时,该模拟也将注入到字段中。

Mockito.mock()

它只是一个代表@Mock


5
别忘了@Mock需要手动调用MockitoRunner或initMocks。
Florian Schaetz '17

4
是唯一的区别@MockBean@Mock一个将注入模拟成Spring ApplicationContext和其他不?
Doug

3
@Doug您对它进行了很好的总结,但需要记住MockBean是Spring Boot的一部分
comiventor
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.