Spy被测对象(SUT)后如何设置声明字段?解决NoSuchFieldException异常
解决Mockito Spy后反射找不到字段的问题
问题原因
当使用Mockito.spy()创建被测对象(SUT)时,实际得到的是Mockito动态生成的代理子类,而非TestedClass本身。调用sut.getClass()返回的是代理类,而getDeclaredField()仅查找当前类声明的字段,不会遍历父类,因此无法找到TestedClass中定义的field1、field2,抛出NoSuchFieldException。
解决方案
方案1:直接从原类获取字段定义
修改反射设置字段的方法,直接基于TestedClass的Class对象获取字段,而非代理类:
private void setSutField(String fieldName, Object value) throws Exception { // 直接使用原被测类的Class,跳过代理类 Field field = TestedClass.class.getDeclaredField(fieldName); field.setAccessible(true); field.set(sut, value); }
这种方法绕开了代理类的限制,直接操作原类的字段定义,确保能找到目标字段。
方案2:正确使用@Spy + @Mock注解实现自动注入
之前的注解用法错误导致初始化失败,正确配置方式如下:
public class TestClass { // @Spy注解:Mockito自动创建TestedClass的Spy实例 @Spy private TestedClass sut; // @Mock注解:自动创建Mock对象 @Mock private SomeClass1 field1; @Mock private SomeClass2 field2; @BeforeMethod public void setup() { // 初始化所有Mockito注解 MockitoAnnotations.openMocks(this); } @Test public void test1() { // Stub protected方法示例 doReturn(mockResult).when(sut).yourProtectedMethod(); // 直接使用sut,field1和field2已自动注入 } }
注意事项:
- @Spy无需手动new实例,Mockito会自动创建;若需自定义实例,可写成
@Spy private TestedClass sut = new TestedClass(); - 必须调用
MockitoAnnotations.openMocks(this)完成注解初始化(或使用MockitoJUnitRunner运行测试) - Mockito会自动按字段名匹配,将@Mock对象注入到@Spy实例的对应public字段中,无需额外配置
方案3:先注入字段再创建Spy
先给真实的TestedClass实例设置Mock字段,再基于该实例创建Spy:
@BeforeMethod public void setup() { // 创建真实实例并注入Mock字段 TestedClass realInstance = new TestedClass(); realInstance.field1 = Mockito.mock(SomeClass1.class); realInstance.field2 = Mockito.mock(SomeClass2.class); // 基于已注入字段的实例创建Spy sut = Mockito.spy(realInstance); // Stub目标protected方法 doReturn(mockValue).when(sut).yourProtectedMethod(); }
这种方式更直观,避免反射操作,同时确保Spy后的对象保留已设置的字段值。
内容的提问来源于stack exchange,提问作者hitesh
相关产品推荐
相关产品推荐

