如何让Mockito在for循环迭代时从列表返回指定值?
用Mockito实现方法每次调用返回列表对应值的方案
这个需求在单元测试里挺常见的,Mockito刚好有几种简洁的实现方式,我给你分享两种实用的方案,你可以根据自己的场景选择:
方案一:直接使用thenReturn传入序列值
如果你的循环次数固定(比如这里的10次),而且提前知道每次要返回的数值列表,直接用thenReturn的可变参数特性就可以搞定。Mockito会按你传入的顺序,每次调用方法时依次返回对应的值。
示例代码:
// 准备好你要依次返回的数值列表 List<Long> expectedValues = Arrays.asList(5L, 3L, 7L, 2L, 9L, 1L, 4L, 6L, 8L, 0L); // mock目标对象 Bar barObj = Mockito.mock(Bar.class); // 把列表转成数组传给thenReturn,Mockito会按顺序返回每个元素 Mockito.when(barObj.getNumFound()).thenReturn(expectedValues.toArray(new Long[0]));
注意点:
- 如果调用次数超过了列表的长度,后续的调用会一直返回列表的最后一个元素(Mockito 2.x及以上版本的行为)。所以如果你的循环次数和列表长度一致,这个方案最省心。
方案二:用Answer接口动态处理调用次数
如果你的调用次数不确定,或者需要更灵活的逻辑(比如根据调用次数做额外判断),可以用Answer接口来动态生成返回值。我们可以用一个计数器来记录方法被调用的次数,每次调用时从列表中取出对应索引的元素。
示例代码:
List<Long> expectedValues = Arrays.asList(5L, 3L, 7L, 2L, 9L, 1L, 4L, 6L, 8L, 0L); Bar barObj = Mockito.mock(Bar.class); // 用原子计数器来记录调用次数(单线程测试用普通int也可以,原子类更安全) AtomicInteger callCounter = new AtomicInteger(0); Mockito.when(barObj.getNumFound()).thenAnswer(invocation -> { int currentIndex = callCounter.getAndIncrement(); // 如果调用次数在列表范围内,返回对应值;否则返回默认值(这里用0L) return currentIndex < expectedValues.size() ? expectedValues.get(currentIndex) : 0L; });
优势:
- 可以灵活处理调用次数超过列表长度的情况,自定义默认返回值或者抛出异常。
- 适合动态生成返回值的场景,比如需要根据调用上下文调整返回结果。
完整测试示例
下面是一个包含循环逻辑的完整单元测试例子,你可以直接参考:
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class BarTest { interface Bar { long getNumFound(); } @Test void testTotalCalculation() { // 1. 准备预期的返回值和预期总和 List<Long> expectedValues = Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L); long expectedTotal = expectedValues.stream().mapToLong(Long::longValue).sum(); // 2. mock Bar对象并设置返回逻辑(二选一即可) Bar barObj = Mockito.mock(Bar.class); // 方案一:用thenReturn Mockito.when(barObj.getNumFound()).thenReturn(expectedValues.toArray(new Long[0])); // 方案二:用Answer // AtomicInteger callCounter = new AtomicInteger(0); // Mockito.when(barObj.getNumFound()).thenAnswer(invocation -> { // int idx = callCounter.getAndIncrement(); // return idx < expectedValues.size() ? expectedValues.get(idx) : 0L; // }); // 3. 执行你的业务逻辑 long total = 0; for (int i = 0; i < 10; i++) { long numFound = barObj.getNumFound(); total += numFound; } // 4. 验证结果和调用次数 Assertions.assertEquals(expectedTotal, total); Mockito.verify(barObj, Mockito.times(10)).getNumFound(); } }
内容的提问来源于stack exchange,提问作者Augusto Dias
相关产品推荐
相关产品推荐

