Mockito:当mock数据的参数是对象时,返回的结果为空或者空集合的解决方案

本文介绍了如何使用Mockito针对对象参数进行智能匹配,避免因参数不一致导致的null或空集合结果。讲解了Mockito.any()和自定义ArgumentMatcher在单元测试中的应用,帮助开发者解决复杂接口调用中的mock问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家都知道通过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中的数据

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值