文章目录
摘要
“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本人原创,转载请附上原文链接)