抽象类不能直接被 mockito.spy,因其无法实例化;必须先创建匿名子类或具体子类实例,再对其 spy,确保目标对象真实存在且非 final 方法可拦截。

抽象类本身不能直接实例化,但 Mockito 的 spy 可以对**已存在的具体对象**进行部分模拟。因此,要对抽象类使用 Mockito.spy,关键不是“spy 抽象类”,而是先创建一个该抽象类的匿名子类(或具体子类)实例,再对其 spy。
为什么不能直接 spy 抽象类?
因为 Mockito.spy() 底层需要一个真实对象作为代理目标。抽象类没有构造器可调用(除非有默认构造且不被强制实现),无法 new 出实例。Mockito 会抛出类似 Cannot instantiate class 的异常。
正确做法:用匿名子类 + spy
在测试中,通过匿名内部类快速实现抽象类(只实现必要的抽象方法),然后对这个实例 spy:
- 只实现最简抽象方法(返回合理默认值或调用
super) - 确保非抽象方法是可执行的(避免空指针或未实现逻辑)
- 用
Mockito.spy(新实例)包装它
示例:
// 假设抽象类
abstract class DataProcessor {
public void logStart() { System.out.println("start"); }
public abstract String process(String input);
public String wrapResult(String s) { return "[OK]" + s; }
}
// 单元测试中
@Test
void testSpyOnAbstractClass() {
// 创建匿名子类实例(仅实现抽象方法)
DataProcessor spy = Mockito.spy(new DataProcessor() {
@Override
public String process(String input) {
return "mocked-" + input; // 或 return super.process(input) 如果父类有默认逻辑
}
});
// 部分 mock:让 process 方法返回固定值,其他方法仍走真实逻辑
Mockito.doReturn("stubbed").when(spy).process("anything");
// 调用真实方法
spy.logStart(); // 输出 "start"(真实调用)
String result = spy.wrapResult(spy.process("test")); // process 被 stub,wrapResult 是真实的
assertEquals("[OK]stubbed", result);
}
替代方案:用具体子类 + spy(更清晰)
如果抽象类已有测试专用的具体子类(比如 TestableDataProcessor),推荐优先使用它,语义更明确、可复用:
- 避免匿名类带来的可读性问题
- 便于在多个测试中复用同一实现
- 可预置状态或依赖(如注入 mock 的 service)
示例:
static class TestableDataProcessor extends DataProcessor {
private final String fixedResult;
TestableDataProcessor(String fixedResult) {
this.fixedResult = fixedResult;
}
@Override
public String process(String input) {
return fixedResult;
}
}
@Test
void testWithConcreteSubclass() {
DataProcessor spy = Mockito.spy(new TestableDataProcessor("from-spy"));
Mockito.doReturn("stubbed-again").when(spy).process("any");
assertEquals("stubbed-again", spy.process("any"));
assertEquals("[OK]stubbed-again", spy.wrapResult("x")); // wrapResult 仍是真实逻辑
}
注意事项与常见坑
使用 spy 时需注意:
- 不要 spy 构造器中抛异常的抽象类:若抽象类构造器含校验逻辑,匿名子类也会触发,应确保构造安全
- final 方法无法被 spy 拦截:Mockito 默认不 mock final 方法(需启用 inline mock maker 并配置)
-
避免过度 spy:如果大部分方法都要 stub,考虑用
mock()+thenCallRealMethod()组合更合适 -
spy 对象的字段不会自动初始化:如有依赖字段,需手动设置(如
spy.field = mockDep)
不复杂但容易忽略细节。核心就一条:spy 的目标必须是一个活的对象,而抽象类需要靠子类来“具身”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











