【问题标题】:Difference between @Mock, @MockBean and Mockito.mock()@Mock、@MockBean 和 Mockito.mock() 之间的区别
【发布时间】:2017-10-27 07:08:00
【问题描述】:

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

  1. @MockBean:

    @MockBean
    MyService myservice;
    
  2. @Mock:

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

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

【问题讨论】:

    标签: java spring spring-boot junit mockito


    【解决方案1】:

    Plain 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 class

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

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

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

    Javadoc 参考:

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

    ...

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


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

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

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

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


    Spring Boot的典型用法@MockBean

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

    The Spring Boot documentation 总结的很好:

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

    【讨论】:

    • 会使用@MockBean 创建bean 的副本并将其注入ApplicationContext 吗?或者模拟 bean 的所有方法是否都为空?如果所有方法都为 null,我可以像使用 @Mock 一样对它们进行存根吗?
    • 如前所述,如果在 Spring 配置中已经定义了声明相同类型的 bean,则使用 @MockBean 将替换应用程序上下文中的 bean。并且注入是在您声明@MockBean. 的类中执行的。 DI 机制以这种方式工作:您在 DI 上下文中注册一个对象,然后您可以将 Spring 上下文中引用的对象注入特定类中。您不会在 DI 上下文中注入对象。
    • 我在关注,但是为什么服务类的覆盖率为 0 %?
    【解决方案2】:

    最后很容易解释。如果您只是查看注释的 javadocs,您会看到不同之处:

    @Mock: (org.mockito.Mock)

    将字段标记为模拟。

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

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

    可用于向 Spring ApplicationContext 添加模拟的注解。可用作类级别注释或@Configuration 类中的字段,或@RunWith SpringRunner 的测试类。

    Mocks 可以按类型或 bean 名称注册。在上下文中定义的任何现有的相同类型的单个 bean 都将被模拟替换,如果没有定义现有的 bean,则将添加一个新的。

    @MockBean 用于字段时,以及在应用程序上下文中注册时,mock 也会被注入到字段中。

    Mockito.mock()

    它只是 @Mock 的表示。

    【讨论】:

    • 我们不要忘记@Mock 需要手动调用 MockitoRunner 或 initMocks。
    • @MockBean@Mock 之间的唯一区别是一个将模拟注入 Spring ApplicationContext 而另一个不会?
    • @Doug 你总结得很好,但需要记住 MockBean 是 Spring Boot 的一部分
    • 要使用@MockBean,您需要使用@RunWith(SpringRunner.class) 注释类。但是,对于使用@Mock,您可以使用@RunWith(MockitoJUnitRunner.class) 并调用@Florian-schaetz 提到的initMocks()。 @Mock 也可以与 SpringRunner 一起使用,但会增加加载 applicationContext 的开销
    猜你喜欢
    • 1970-01-01
    • 2014-06-27
    • 1970-01-01
    • 1970-01-01
    • 2016-09-07
    • 2020-10-21
    • 2018-12-10
    • 2017-10-15
    相关资源
    最近更新 更多