You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:12:48