Mockito验证可变对象失效:如何不修改被测类完成测试?
解决Mockito验证对象后续修改导致断言失败的问题
在测试场景中,由于processOne调用后,同一个A对象的属性被修改为2,而Mockito的argThat是在验证阶段才读取对象当前值,导致验证processOne的参数时得到的是修改后的值2,而非调用时的1。以下是两种无需修改被测类A、B、C的解决方案:
方法一:使用doAnswer拦截调用并即时记录参数状态
通过doAnswer拦截B.processOne的调用,在方法执行时立即获取当时A对象的属性值并保存,后续直接验证保存的值即可:
import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; class CTest { @Test void testSet() { B b = mock(B.class); C c = new C(b); // 用数组保存调用时的属性值(Lambda中无法修改非final局部变量) int[] processOneAValue = new int[1]; // 拦截processOne调用,记录调用时刻的a值 doAnswer(invocation -> { A arg = invocation.getArgument(0); processOneAValue[0] = arg.getA(); return null; }).when(b).processOne(any(A.class)); c.run(); // 验证processOne被调用,且当时的a值为1 verify(b).processOne(any(A.class)); assertEquals(1, processOneAValue[0]); // 验证processTwo的参数值为2 verify(b).processTwo(argThat(a -> a.getA() == 2)); } }
方法二:使用自定义ArgumentMatcher捕获调用时的状态
自定义Matcher,在匹配过程中记录调用时的属性值,后续验证该记录值:
import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import org.mockito.ArgumentMatcher; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; class CTest { @Test void testSet() { B b = mock(B.class); C c = new C(b); AtomicInteger capturedProcessOneA = new AtomicInteger(); // 定义自定义Matcher,捕获调用时的a值 ArgumentMatcher<A> processOneMatcher = a -> { capturedProcessOneA.set(a.getA()); return true; // 允许匹配通过,重点是捕获值 }; c.run(); // 验证processOne被调用,同时触发Matcher捕获值 verify(b).processOne(argThat(processOneMatcher)); assertEquals(1, capturedProcessOneA.get()); // 验证processTwo的参数 verify(b).processTwo(argThat(a -> a.getA() == 2)); } }
原理说明
两种方法的核心都是在processOne被调用的时刻就捕获并保存A对象的属性值,而非等到验证阶段才读取对象的当前状态,从而避开了对象后续被修改的影响。
内容的提问来源于stack exchange,提问作者levant pied
相关产品推荐
相关产品推荐

