且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2022-06-26 08:29:14

Plain Mockito 库

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

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

来自 Mockito 库并且在功能上是等效的.
它们允许模拟类或接口并记录和验证其行为.

come from the Mockito library and are functionally equivalent.
They allow to mock a class or an interface and to record and verify behaviors on it.

使用注释的方式更短,因此更可取,而且通常更受欢迎.

The way using annotation is shorter, so preferable and often preferred.

请注意,要在测试执行期间启用 Mockito 注释,MockitoAnnotations.initMocks(this) 静态方法必须被调用.
为避免测试之间的副作用,建议在每次测试执行之前进行:

Note that to enable Mockito annotations during test executions, the MockitoAnnotations.initMocks(this) static method has to be called.
To avoid side effect between tests, it is advised to do it before each test execution :

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

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

Another way to enable Mockito annotations is annotating the test class with @RunWith by specifying the MockitoJUnitRunner that does this task and also other useful things :

@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 库中.

The class is included in the spring-boot-test library.

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

It allows to add Mockito mocks in a Spring ApplicationContext.
If a bean, compatible with the declared class exists in the context, it replaces it by the mock.
If it is not the case, it adds the mock in the context as a bean.

Javadoc 参考:

Javadoc reference :

可用于向 Spring 添加模拟的注解应用程序上下文.

Annotation that can be used to add mocks to a Spring ApplicationContext.

...

如果上下文中定义了任何现有的相同类型的单个 bean将被模拟替换,如果没有现有的 bean 被定义一个新的将被添加.

If any existing single bean of the same type defined in the context will be replaced by the mock, if no existing bean is defined a new one will be added.

何时使用经典/纯 Mockito 以及何时使用 Spring Boot 中的 @MockBean?

单元测试旨在独立于其他组件来测试一个组件,并且单元测试还有一个要求:在执行时间方面尽可能快,因为这些测试可能每天在开发人员机器上执行数十次.

Unit tests are designed to test a component in isolation from other components and unit tests have also a requirement : being as fast as possible in terms of execution time as these tests may be executed each day dozen times on the developer machines.

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

Consequently, here is a simple guideline :

当您编写不需要来自 Spring Boot 容器的任何依赖项的测试时,经典/普通的 Mockito 是遵循的方式:它速度快并且有利于被测试组件的隔离.
如果您的测试需要依赖 Spring Boot 容器并且您还想添加或模拟容器 bean 之一:Spring Boot 中的 @MockBean 就是这样.

As you write a test that doesn't need any dependencies from the Spring Boot container, the classic/plain Mockito is the way to follow : it is fast and favors the isolation of the tested component.
If your test needs to rely on the Spring Boot container and you want also to add or mock one of the container beans : @MockBean from Spring Boot is the way.

Spring Boot的典型用法@MockBean

当我们编写一个带有 @WebMvcTest 注释的测试类(网络测试切片)时.

As we write a test class annotated with @WebMvcTest (web test slice).

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

The Spring Boot documentation summarizes that very well :

@WebMvcTest 通常会被限制在单个控制器中并用于结合 @MockBean 提供模拟实现需要的合作者.

Often @WebMvcTest will be limited to a single controller and used in combination with @MockBean to provide mock implementations for required collaborators.

这是一个例子:

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"));
    }

}