项目是spring boot 底座,在进行单元测试的时候,使用到了mock。
参考内容:https://blog.youkuaiyun.com/qq_36688143/article/details/97393949
测试需求:
1.单元测试不依赖第三方组件(包括数据库)
2.单元测试对于第三方自研发接口或者非本组内单元,进行屏蔽
分析:
spring boot 一般的单元测试需要运行在容器内,我们会采取如下步骤进行单元测试,示范:
1.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2.测试类
package com.xxx.service;
import com.xxxx.domain.LearnResource;
import org.junit.Assert; import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.hamcrest.CoreMatchers.*;
@RunWith(SpringRunner.class)
@SpringBootTest
public class LearnServiceTest {
@Autowired
private LearnService learnService;
@Test
public void getLearn(){
LearnResource learnResource=learnService.selectByKey(1001L);
Assert.assertThat(learnResource.getAuthor(),is("xxxxx")); } }
存在问题:
使用了容器,在@RunWith上面我们一般会使用自己的运行类,这样子不满足需求,第一点,我们会去初始化数据库,第二点,我们对于第三方注入没有很好的挡板,导致不是我们的测试对象也进行了运行,可能会遇到第三方未知错误。
改善示例:
解释:1.@RunWith(SpringRunner.class)声明在Spring的环境中进行单元测试,这样Spring的相关注解就会被识别并起效
2.用@SpringBootTest,它会扫描应用程序的spring配置,并构建完整的Spring Context
3.通过@SpringBootTest我们可以指定启动类,或者给@SpringBootTest的参数webEnvironment赋值为SpringBootTest.WebEnvironment.RANDOM_PORT,这样就会启动web容器,并监听一个随机的端口,同时,为我们自动装配一个TestRestTemplate类型的bean来辅助我们发送测试请求
优化:1.不使用@Autowired
2.不启动容器
3.脱离数据库
@RunWith(MockitoJUnitRunner.class)
@Sld4j
public class test{
@InjectMocks
private xxxxx xxxx;-----目标对象(只能是实现,不能是接口)
@Mock
private xxxxx xxxx; ----非本测试对象,但是需要依赖的内容
@Test
public void Test(){
//TODO 这里写测试逻辑
}
}
解释一下:
a.@Mock 下面注入的内容,如果没有返回值需要接收,或者测试对象不需要返回值继续后续的逻辑,那我们已经把第三发的内容屏蔽了,你不用关心了。
b.@Mock 下面注入的内容,测试对象需要其返回值,返回值影响后续的操作,那我们一般这样做。
Mockito.when(Mock下面的内容).thenReturn(xxx); xxx------指的是特定的测试需要的对象和属性值。也就是在你运行的时候需要的内容,然后辅助你的案例继续往后运行。
可能遇到的问题:
1.SpringContextUtil获取bean报错空指针,也就是说这个你没处理好,可以参照这个处理:
@Mock
ApplicationContext appliactionContext;
@Test
public void Test(){
new SpringContextUtil.setAppliactionContext(appliactionContext);//这样我们就把mock对象给到了这个类型里面。
}
2.方法中存在@Value 这样的写法,主要是注入了配置文件的值,那么我们这样去做
ReflectionTestUtils.setField(测试对象,"@Value下面的key",指定你需要的value);
这样写法,我们就可以给到指定的值了。
3.上下文为空
提供一个思路,看上下文的写法。一般是提供一个instance,之后再进行instance相关操作。
@Mock
ServiceContext serviceContext;---------先mock你的上下文,理解成一个对象
ReflectionTestUtils.setField(new ServiceContext(),"instance",serviceContext);-----这样就把对象给了我们的上下文。