ChatGPT解决这个技术问题 Extra ChatGPT

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

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

@MockBean:@MockBean MyService 我的服务; @Mock:@Mock MyService 我的服务; Mockito.mock() MyService myservice = Mockito.mock(MyService.class);


d
davidxxx

普通的 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,则它用模拟替换它。
如果是并非如此,它在上下文中添加模拟作为 bean。

Javadoc 参考:

可用于将模拟添加到 Spring ApplicationContext 的注释。 ...如果在上下文中定义的任何现有的相同类型的单个 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 的所有方法是否都为空?如果所有方法都为空,我可以像使用 @Mock 一样对它们进行存根吗?
如前所述,如果在 Spring 配置中已经定义了声明相同类型的 bean,则使用 @MockBean 将替换应用程序上下文中的 bean。并且注入在您声明的类中执行 @MockBean. DI 机制以这种方式工作:您在 DI 上下文中注册一个对象,然后您可以将 Spring 上下文中引用的对象注入特定类中。您不会在 DI 上下文中注入对象。
我正在关注,但为什么服务类的覆盖率为 0%?
@MockBean 的问题在于它不适用于 Mockito.verify
P
Patrick

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

@Mock:(org.mockito.Mock

将字段标记为模拟。

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

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

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

Mocks 可以按类型或 bean 名称注册。在上下文中定义的任何现有的相同类型的单个 bean 都将被 mock 替换,如果没有定义现有的 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 的开销
P
Procrastinator

Mocktio.mock() :-

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

@Mock:-

这是 mock() 方法的简写,因此它更可取且经常使用。 mock() 和 @Mock 在功能上是等效的。由于字段名称出现在错误消息中,因此更容易识别模拟失败中的问题。

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

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

@MockBean :- 用于将模拟对象添加到 Spring 应用程序上下文中。这个模拟将替换应用程序上下文中现有的相同类型的 bean。如果没有可用的 bean,则将添加新的 bean。这在集成测试用例中很有用。

当我们编写一个不需要来自 Spring Boot 容器的任何依赖项的测试用例时,会使用经典/普通的 Mockito,它速度快并且有利于测试组件的隔离。

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