前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >@Spy、@SpyBean、@MockBean、@Mock、@RunWith、@ExtendWith对比

@Spy、@SpyBean、@MockBean、@Mock、@RunWith、@ExtendWith对比

作者头像
查拉图斯特拉说
发布2023-12-19 09:56:46
3740
发布2023-12-19 09:56:46
举报
文章被收录于专栏:后端架构后端架构

前言

在写单元测试中经常会用到Mockito,但是这些类似的注解非常混乱,今天总结一下相关的注解,说明其中的含义和实现例子。

Mockito.mock() vs @Mock vs @MockBean

Mockito.mock ()方法允许我们创建类或接口的模拟对象。

代码语言:javascript
复制
@Test
public void givenCountMethodMocked_WhenCountInvoked_ThenMockedValueReturned() {
    UserRepository localMockRepository = Mockito.mock(UserRepository.class);
    Mockito.when(localMockRepository.count()).thenReturn(111L);

    long userCount = localMockRepository.count();

    Assert.assertEquals(111L, userCount);
    Mockito.verify(localMockRepository).count();
}

@Mock该注释是Mockito.mock()方法的简写。需要注意的是,我们应该只在测试类中使用它。与mock()方法不同的是,我们需要启用Mockito注解才能使用该注解。

代码语言:javascript
复制
@RunWith(MockitoJUnitRunner.class)
public class MockAnnotationUnitTest {
    
    @Mock
    UserRepository mockRepository;
    
    @Test
    public void givenCountMethodMocked_WhenCountInvoked_ThenMockValueReturned() {
        Mockito.when(mockRepository.count()).thenReturn(123L);

        long userCount = mockRepository.count();

        Assert.assertEquals(123L, userCount);
        Mockito.verify(mockRepository).count();
    }
}

@MockBean将模拟对象添加到 Spring 应用程序上下文中。模拟将替换应用程序上下文中相同类型的任何现有 bean。

代码语言:javascript
复制
@RunWith(SpringRunner.class)
public class MockBeanAnnotationIntegrationTest {
    
    @MockBean
    UserRepository mockRepository;
    
    @Autowired
    ApplicationContext context;
    
    @Test
    public void givenCountMethodMocked_WhenCountInvoked_ThenMockValueReturned() {
        Mockito.when(mockRepository.count()).thenReturn(123L);

        UserRepository userRepoFromContext = context.getBean(UserRepository.class);
        long userCount = userRepoFromContext.count();

        Assert.assertEquals(123L, userCount);
        Mockito.verify(mockRepository).count();
    }
}

JUnit5 @RunWith annotation with the new @ExtendWith

在 JUnit 5 中,@RunWith注释已被更强大的@ExtendWith注释取代。

@RunWith

@RunWith注释在任何较旧的 JUnit 环境中运行 JUnit 5 测试。

JUnitPlatform类是一个基于 JUnit 4 的运行器,允许我们在 JUnit 平台上运行 JUnit 4 测试。

代码语言:javascript
复制
@RunWith(JUnitPlatform.class)
public class GreetingsUnitTest {
    // ...
}

基于 JUnit 4 的运行器的测试迁移到 JUnit 5。我们将使用 Spring 测试作为示例:

代码语言:javascript
复制
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { SpringTestConfiguration.class })
public class GreetingsSpringUnitTest {
    // ...
}

@ExtendWith

测试迁移到 JUnit 5,我们需要用新的@ExtendWith替换@RunWith注释:

代码语言:javascript
复制
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { SpringTestConfiguration.class })
public class GreetingsSpringUnitTest {
    // ...
}

@Spy 和 @SpyBean 之间的区别

@Spy注释是 Mockito 测试框架的一部分,它创建真实对象的间谍(部分模拟),通常用于单元测试。

代码语言:javascript
复制
@Spy
OrderRepository orderRepository;
@Spy
NotificationService notificationService;
@InjectMocks
OrderService orderService;

@Test
void givenNotificationServiceIsUsingSpy_whenOrderServiceIsCalled_thenNotificationServiceSpyShouldBeInvoked() {
    UUID orderId = UUID.randomUUID();
    Order orderInput = new Order(orderId, "Test", 1.0, "17 St Andrews Croft, Leeds ,LS17 7TP");
    doReturn(orderInput).when(orderRepository)
        .save(any());
    doReturn(true).when(notificationService)
        .raiseAlert(any(Order.class));
    Order order = orderService.save(orderInput);
    Assertions.assertNotNull(order);
    Assertions.assertEquals(orderId, order.getId());
    verify(notificationService).notify(any(Order.class));
}

Spring Boot 的@SpyBean注解

@SpyBean注解是Spring Boot特有的,用于与Spring的依赖注入进行集成测试

代码语言:javascript
复制
@Autowired
OrderRepository orderRepository;
@SpyBean
NotificationService notificationService;
@SpyBean
OrderService orderService;

@Test
void givenNotificationServiceIsUsingSpyBean_whenOrderServiceIsCalled_thenNotificationServiceSpyBeanShouldBeInvoked() {

    Order orderInput = new Order(null, "Test", 1.0, "17 St Andrews Croft, Leeds ,LS17 7TP");
    doReturn(true).when(notificationService)
        .raiseAlert(any(Order.class));
    Order order = orderService.save(orderInput);
    Assertions.assertNotNull(order);
    Assertions.assertNotNull(order.getId());
    verify(notificationService).notify(any(Order.class));
}
@Spy@SpyBean之间的区别

在单元测试中,我们使用@Spy,而在集成测试中,我们使用@SpyBean

如果@Spy注解的组件包含其他依赖项,我们可以在初始化时声明它们。如果在初始化期间未提供它们,系统将使用零参数构造函数(如果可用)。在@SpyBean测试的情况下,我们必须使用@Autowired注释来注入依赖组件。否则,在运行时,Spring Boot 会创建一个新实例。

如果我们在单元测试示例中使用 @SpyBean ,则 当 调用NotificationService时,测试将失败并出现NullPointerException因为OrderService需要模拟/间谍 NotificationService

同样,如果在集成测试的示例中使用@Spy ,则测试将失败并显示错误消息“Wanted but not invoked: notificationService.notify(<any com.baeldung.spytest.Order> )”,因为 Spring 应用程序context 不知道 @Spy注解的类相反,它创建一个新的NotificationService实例并将其注入到OrderService 中。

@SpyBean需要手动注入bean,但是@Spy 不需要,除非你调用了依赖

总结

@Spy、@SpyBean、@MockBean、@Mock、@RunWith、@ExtendWith,带bean的就跟集成测试有关,例如集成Spring,如果只是简单的单元测试可以配置不带Bean的,这里面最好区分的还是@RunWith和@ExtendWith,一个是JUnit4一个是JUnit5。

点赞关注评论一键三连,欢迎关注公众号【i查拉图斯特拉如是说】每周分享技术干货、开源项目、实战经验、国外优质文章翻译等,您的关注将是我的更新动力!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-12-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • Mockito.mock() vs @Mock vs @MockBean
  • JUnit5 @RunWith annotation with the new @ExtendWith
  • @Spy 和 @SpyBean 之间的区别
    • @Spy和@SpyBean之间的区别
    • 总结
    相关产品与服务
    腾讯云服务器利旧
    云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档