一、什么是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");
}
}
这种编写的测试用例需要启动依靠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);
}
}