目录
大家都知道通过Mockito 可以mock数据,当参数是字符串或者基本数据类型时,没有问题,但是当参数是对象时返回结果可能是null或者集合的长度为0,如何解决呢?
1、先看一个例子
接口的实现类
@Service("firstService")
public class FirstServiceImpl implements FirstService {
@Resource
FirstDao firstDao;
@Override
public List<User> find(User user) {
List<User> list= firstDao.find(user);
return list;
}
}
单元测试
@RunWith(SpringJUnit4ClassRunner.class)
public class FirstServiceTest {
@Mock
FirstDao firstDao;
@InjectMocks
FirstService firstService=new FirstServiceImpl();
@Test
public void getUserTest1(){
//准备mock返回的数据
User user = new User();
user.setId(1L);
user.setName("姓名");
user.setAge("16");
List<User> userList=new ArrayList<>();
userList.add(user);
//mock服务或者类中的某个方法,当参数是什么时,返回值是什么
Mockito.when(firstDao.find(user)).thenReturn(userList);
User user1 = new User();
user1.setId(1L);
user1.setName("姓名");
user1.setAge("16");
//执行单元测试逻辑
List<User> users = firstService.find(user1);
//断言
Assert.assertFalse(users.size()<1);
}
}
如果只是这样没问题,但是在实际开发中,一般接口内的实现不会这么简单,对于业务复杂的场景,经常多个方法或者多个接口得出一个结果,而多个方法和接口之间还涉及到中间临时对象的使用,下面就先展示一个错误的例子
2、这是个错误的例子
接口的实现类
@Service("firstService")
public class FirstServiceImpl implements FirstService {
@Resource
FirstDao firstDao;
@Resource
SecondDao secondDao;
@Override
public List<User> find(User user) {
List<User> list= firstDao.find(user);
User user1 = new User();
user1.setName(list.get(0).getName());
user1.setAge(list.get(0).getAge());
return secondDao.find(user1);
}
}
单元测试
@RunWith(SpringJUnit4ClassRunner.class)
public class FirstServiceTest {
@Mock
FirstDao firstDao;
@Mock
SecondDao secondDao;
//上面mock的数据需要注入到哪里
@InjectMocks
FirstService firstService=new FirstServiceImpl();
@Test
public void getUserTest1(){
//准备mock返回的数据
User user = new User();
user.setId(1L);
user.setName("姓名");
user.setAge("16");
List<User> userList=new ArrayList<>();
userList.add(user);
//mock服务或者类中的某个方法,当参数是什么时,返回值是什么
Mockito.when(firstDao.find(user)).thenReturn(userList);
Mockito.when(secondDao.find(user)).thenReturn(userList);
User user1 = new User();
user1.setId(1L);
user1.setName("姓名");
user1.setAge("16");
//执行单元测试逻辑
List<User> users = firstService.find(user1);
//断言,
Assert.assertFalse("List集合的size小于1,不符合预期",users.size()<1);
}
}
根据这里的断言就知道此次单元测试返回的结果不符合我们的预期,这是因为在FirstServiceImpl
类中有一个临时的user1
,从第一个接口中得到结果拼接成一个临时对象当参数运行第二个方法,这种场景非常正常
你或许有疑问?为什么明明在单测已经对secondDao.find
进行了mock,但是返回的结果集合长度为0(如果是对象,此时返回的对象则是null
)
这是因为虽然你在单测中已经mock数据,但是mock数据的参数与在实际单测执行时大参数不是一个对象,当然有个解决方案是重写hashCode
,但是这样就太low了,实际上Mockito提供了两种(可能有多种,目前我只用到了这两种)可以解决此问题的方法
3、 Mockito.when时参数用Mockito.any(实际参数对象的class)
@RunWith(SpringJUnit4ClassRunner.class)
public class FirstServiceTest {
@Mock
FirstDao firstDao;
@Mock
SecondDao secondDao;
//上面mock的数据需要注入到哪里
@InjectMocks
FirstService firstService=new FirstServiceImpl();
@Test
public void getUserTest1(){
//准备mock返回的数据
User user = new User();
user.setId(1L);
user.setName("姓名");
user.setAge("16");
List<User> userList=new ArrayList<>();
userList.add(user);
//mock服务或者类中的某个方法,当参数是什么时,返回值是什么
Mockito.when(firstDao.find(user)).thenReturn(userList);
//find(User user),这里mock的参数用Mockito.any(User.class)代替user
Mockito.when(secondDao.find(Mockito.any(User.class))).thenReturn(userList);
User user1 = new User();
user1.setId(1L);
user1.setName("姓名");
user1.setAge("16");
//执行单元测试逻辑
List<User> users = firstService.find(user1);
//断言,
Assert.assertEquals(1, users.size());
}
}
这个问题可以解决多个接口之间使用中间临时对象返回结果为null或者集合长度为0的情况,因为在执行这个单测的过程中,secondDao.find
返回结果都是userList
,不管User中的字段是什么,都是一个结果,这个已经解决了很多问题,但是如果你的一个接口是否执行是根据上一个接口的返回结果进行判断,这里就不满足了,但是下面可以满足
4、 Mockito.when时重写静态argThat中ArgumentMatcher对象中的matches方法
可以看看mockito-argument-matcher,里面有解释
单元测试代码
@RunWith(SpringJUnit4ClassRunner.class)
public class FirstServiceTest {
@Mock
FirstDao firstDao;
@Mock
SecondDao secondDao;
//上面mock的数据需要注入到哪里
@InjectMocks
FirstService firstService=new FirstServiceImpl();
@Test
public void getUserTest1(){
//准备mock返回的数据
User user = new User();
user.setId(1L);
user.setName("姓名");
user.setAge("16");
List<User> userList=new ArrayList<>();
userList.add(user);
//mock服务或者类中的某个方法,当参数是什么时,返回值是什么
Mockito.when(firstDao.find(user)).thenReturn(userList);
Mockito.when(secondDao.find(argThat(new ArgumentMatcher<User>() {
@Override
public boolean matches(User argument) {
//如果返回true则返回thenReturn中的userList,否则对象返回null,集合返回长度为0的集合
if(argument.getName().equals("姓名")){
return true;
}
return false;
}
}))).thenReturn(userList);
User user1 = new User();
user1.setId(1L);
user1.setName("姓名");
user1.setAge("16");
//执行单元测试逻辑
List<User> users = firstService.find(user1);
//断言,
Assert.assertEquals(1, users.size());
}
}
这样方法的返回的结果就可以自定义根据对象中某些字段的值是否符合自己的要求决定是否返回thenReturn
中的数据