springboot使用Spy、SpyBean实现对象的部分模拟

摘要

“Mockito + springboot” 搞定UT用例的复杂场景数据模拟2.0~

一、背景

上一篇文章(springboot使用Mockito和Junit进行测试代码编写)对springboot1.X+junit4和springboot2.X+junit5这两种场景进行展开介绍,这篇文章针对springboot2.x的用法进一步进行展开。

二、概要介绍

经常使用springboot的同学应该知道,springboot的开发过程中离不开对注解的使用,对应UT用例同样是一样,本文将对一些常见的注解进行说明,而且易混点也进行说明~

三、知识点

  • @InjectMocks:可以注入/实例化bean,但是不能注入/实例化其dependency的bean,其dependency的bean为null(可以搭配@AutoWired实现自动化实例化),没有mockitoInterceptor属性,只有本身的成员变量。其dependency用以及dependency的dependency都用@Mock注解时,就可以也被注入了。

  • @Mock:可以注入/实例化bean,但是不会实例化该bean的dependency,其dependency的bean为null。 会多一个属性:mockitoInterceptor。

  • @MockBean:可以注入/实例化bean,会实例化该bean的dependency, 但是不能注入/实例化其dependency的bean,其dependency的bean为null。会多一个属性:mockitoInterceptor。

  • @Spy、@SpyBean: 类似@Mock、@MockBean, 不过是执行的时候能够执行真实的函数。

    补充说明:多一个属性mockitoInterceptor相对于给我们给数据模拟的对象穿了件"衣服", 我们可以对穿衣服的对象进行严格的判定,达到我们对预期效果判断的效果。

    下面我画个图讲一下@Mock/@MockBean的区分
    在这里插入图片描述

    具体对应的代码使用如下:

    /**
     * 测试mock
     */
    @ActiveProfiles({"test"})
    @SpringBootTest(classes = {UsePgApplication.class})
    @ExtendWith(SpringExtension.class)
    @ExtendWith({MockitoExtension.class})
    class AControllerTest {
    
        @Autowired
        @InjectMocks
        private AController aController;
    
        @Mock
        private PersonService personService;
    
        @Test
        void hi() {
            Mockito.doReturn("hjk").when(personService).hi();
    
            assertEquals("hjk", aController.hi());
        }
    
        @Test
        void bye() {
            assertNull(aController.bye());
        }
    }
    
    /**
     * 测试mockBean
     */
    @ActiveProfiles({"test"})
    @SpringBootTest(classes = {UsePgApplication.class})
    @ExtendWith(SpringExtension.class)
    @ExtendWith({MockitoExtension.class})
    class AControllerTest2 {
    
        @Autowired
        private AController aController;
    
        @MockBean
        private CommonFeign commonFeign;
    
        @Test
        void test() {
            Mockito.doReturn("hjk").when(commonFeign).test();
    
            assertEquals("hjk", aController.test());
        }
    }
    

四、Spy、SpyBean实现“功能部分模拟”

4.1 spy跟mock的差异点

1、@Mock创建的是全部mock的对象,即在对具体的方法打桩之前,mock对象的所有属性和方法全被置空(0或null); 与之对应的是@Spy可以创建部分mock的对象,部分mock对象的所有成员方法都会按照原方法的逻辑执行,直到被打桩放回具体的值;

2、Mockito的mock()方法功能与@Mock相同,只是使用方式和场景不同。同样的,@Spy也对应一个spy()方法;

3、@Mock和@Spy注解的对象,均可被@injectMock注入待处理的对象中。

4.2 功能部分模拟的场景

使用@Spy+@Autowired注解时,会调用所有的依赖对象的正式方法实现,如果只使用@Spy,则调用其依赖对象的真实方法会放回null指针,因为没有进行对象的自动化初始化。

4.3 解决方法

4.3.1 使用springboot自带的SpyBean注解作为注释
/**
 * 测试spyBean
 */
@ActiveProfiles({"test"})
@SpringBootTest(classes = {UsePgApplication.class})
@ExtendWith(SpringExtension.class)
@ExtendWith({MockitoExtension.class})
class AControllerTest3 {

    @Autowired
    private AController aController;

    @SpyBean
    private CommonFeign commonFeign;

    @Test
    void hi() {
        // 不会真实执行commonFeign.test()
        Mockito.doReturn("hjk").when(commonFeign).test();
        // 会真实执行commonFeign.test()
        // Mockito.when(commonFeign.test()).thenReturn("hjk");
        assertEquals("hjk", aController.test());
    }
}
4.3.2 使用java反射“自动装配”间谍对象,ReflectionTestUtils
/**
 * 测试spy, 使用ReflectionTestUtils
 */
@ActiveProfiles({"test"})
@SpringBootTest(classes = {UsePgApplication.class})
@ExtendWith(SpringExtension.class)
@ExtendWith({MockitoExtension.class})
class AControllerTest4 {

    @Autowired
    private AController aController;

    @Autowired
    private CommonFeign commonFeign;

    @Test
    void hi() {
        CommonFeign spy = Mockito.spy(commonFeign);
        Mockito.doReturn("hjk").when(spy).test();
        ReflectionTestUtils.setField(aController, "commonFeign", spy);

        assertEquals("hjk", aController.test());
    }
}

补充说明:

这个场景可能会报错如下:

org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class com.sun.proxy.$Proxy70
Mockito cannot mock/spy because :
 - final class

这个时候需要添加pom模块即可解决

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
        </dependency>

4.4 代码链接:

以上代码均经过测试验证,戳→

https://github.com/junkaitongxue/LearnSpringBoot/tree/main/usePostgres/src/test/java/com/dreamkite/pg/controller

五、相关链接:

(以上内容为DreamKite本人原创,转载请附上原文链接)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dream_Kite

你的鼓励,是我不断创作的动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值