提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
一、什么是mock
- 简单来说就是在Junit Test中,环境的影响,对于代码逻辑的测试中有关数据库操作的测试、mq环境的配置都是比较困难的,而且执行起来效率很低
- mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。
二、如何使用
1、引入依赖
默认现在的spring-boot-starter-test中包含了mock的包,所以无需多余引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2、对于一个普通的类做测试
- 假设原代码如下,其中省略一些逻辑代码
@Service
public class userService{
@Autowired
private UserMapper userMapper;
public User getUser(String id){
...
...
User u = userMapper.getUser(id);
...
...
}
}
- 普通的测试用例编写如下
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class UserServiceTest {
@Autowired
UserService userService;
@Test
public void testGetUser(){
userService.getUser("11111");
}
}
mock案例
- 这种编写的测试用例需要启动依靠spring,但是我们主要想测试的是代码逻辑不需要依赖于spring的bean注入,而且如果测试用例多了,整个项目的测试十分耗时,
- 所以引入了mock的使用,不依赖于spring的bean注入,改为模拟bean对象,并且可以设置bean对象方法的返回值
该测试用例不需要依赖@SpringBootTest启动非常快,还可以减少安装各种中间件的时间
@RunWith(SpringRunner.class)
public class UserServiceTest {
@InjectMocks //创建一个实例,这个实例可以调用真实的方法,并且可以使用mock当中的对象来使用
UserService userService;
@Mock
UserMapper userMapper;//模拟对象,并非spring注入的bean
@Test
public void testGetUser(){
String id="11111"; //设定一个测试的id
User user=new User();
//模拟getUser方法,并且返回一个自定义的user对象
Mockito.when(userMapper.getUser(id)).thenReturn(user);
//这里id需要与mock测试的id保持一致,测试方法保持一致嘛,不一致会导致上面的mockito....方法失效
userService.getUser(id);
}
3、对于一个没有返回值的方法的测试
@Service
public class userService{
@Autowired
private UserMapper userMapper;
public void saveUser(User user){
...
...
userMapper.saveUser(user);
...
...
}
}
- mock测试如下
@RunWith(SpringRunner.class)
public class UserServiceTest {
@InjectMocks //创建一个实例,这个实例可以调用真实的方法,并且可以使用mock当中的对象来使用
UserService userService;
@Mock
UserMapper userMapper;//模拟对象,并非spring注入的bean
@Test
public void testSaveUser(){
User user=new User();
Mockito.doNothing.when(userMapper).saveUser(user);
userService.saveUser(user);
}
4、对于一些有@Value引入的参数的处理
@Service
public class userService{
@Value("${demo.addr}")
private String addr;
@Autowired
private UserMapper userMapper;
public void saveUser(User user){
...
...
user.setAddr(addr);
userMapper.saveUser(user);
...
...
}
}
- mock测试用例如下
@RunWith(SpringRunner.class)
public class UserServiceTest {
@InjectMocks //创建一个实例,这个实例可以调用真实的方法,并且可以使用mock当中的对象来使用
UserService userService;
@Mock
UserMapper userMapper;//模拟对象,并非spring注入的bean
@Test
public void testSaveUser(){
//设置类属性的默认值,避免@Value获取addr为null导致的代码错误问题
ReflectionTestUtils.setField(userService, "addr", "本地人");
User user=new User();
Mockito.doNothing.when(userMapper).saveUser(user);
userService.saveUser(user);
}
5、对于一些final类抽象类的模拟
- 对于一些final类、抽象类等mock默认无法模拟实例,需要引入一个新的依赖
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
对于一个有final的引入的类
public final class RedisUtil{...}
@Service
public class DemoService{
@Autowired
private RedisUtil redisUtil;
public User getUser(String id){
return redisUtil.getUserById(id);
}
}
- 使用mock如下
@RunWith(PowerMockRunner.class)
@PrepareForTest(value={RedisUtil.class}) //需要把final的对象放进去
@PowerMockIgnore({"sun.security.*", "javax.net.*"})
public class DemoServiceTest{
@InjectMocks
DemoService demoService;
@Mock
private RedisUtil redisUtil;
@Test
public void getUserTest(){
String id="11111";
PowerMockito.when(redisUtil.getUserById(id)).thenReturn(null);
demoService.getUser(id);
}
}
6、静态Static
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
/**
* 反射运行所有的VO属性和方法
*/
@Slf4j
@RunWith(PowerMockRunner.class)
@PrepareForTest(value = WechatInfoProperties.class)
public class WsapTestControllerTest extends BaseTest {
@InjectMocks
private WsapTestController controller;
@Mock
private WsapClient wsapClient;
@Mock
private WxMediaService wxMediaService;
/**
* 在执行测试方法之前要执行的方法
*/
@Before
public void before() {
//模拟对应的依赖对象
MockitoAnnotations.initMocks(this);
}
@Test
public void getTmplMsgList() {
PowerMockito.mockStatic(WechatInfoProperties.class);
WechatInfoProperties.WechatInfoModel wechatInfoModel = new WechatInfoProperties.WechatInfoModel();
wechatInfoModel.setAppId("xxxx");
PowerMockito.when(WechatInfoProperties.getByAlias("weiyinhang")).thenReturn(wechatInfoModel);
PowerMockito.when(wsapClient.getMpTemplates(any())).thenReturn(new ArrayList<>());
controller.getTmplMsgList();
}
}