
本文详解 Mockito 中 mock void 方法的核心机制,重点解决 Only void methods can doNothing() 错误——该错误通常并非因方法非 void 引起,而是因目标方法被声明为 final,需启用 Inline Mock Maker 并配合 doXxx() 族方法才能正确 stub。
本文详解 mockito 中 mock void 方法的核心机制,重点解决 `only void methods can donothing()` 错误——该错误通常并非因方法非 void 引起,而是因目标方法被声明为 `final`,需启用 inline mock maker 并配合 `doxxx()` 族方法才能正确 stub。
在使用 Mockito 进行 Java 单元测试时,遇到 "Only void methods can doNothing()!" 异常却百思不解?尤其当被 mock 的方法明确声明为 void(如 SQLServerPreparedStatement.setStructured(...)),仍报此错——这几乎可以断定:该方法是 final 的。
Mockito 默认仅支持对非 final 类/方法进行 mock。而 com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement 是微软官方 JDBC 驱动中的类,其 setStructured 方法被标记为 final(可通过反编译或 Javadoc 确认)。Mockito 4.x 及以上版本在检测到 final 方法时,会拒绝执行 doNothing() 等 stub 操作,并抛出该提示性异常——它并非误判返回类型,而是主动拦截不安全的 mock 尝试。
✅ 正确解决方案分三步:
1. 启用 Mockito Inline Mock Maker
在 test/resources/mockito-extensions/org.mockito.plugins.MockMaker 文件中,新增一行:
mock-maker-inline
⚠️ 注意:文件路径必须严格匹配,文件名不可带后缀(如
.txt),内容仅此一行,无空格/注释。
同时确保依赖已升级至支持 inline mock 的版本(你当前使用的 mockito-core:4.11.0 完全兼容)。
2. 使用 doXxx() 语法(禁止 when().thenReturn())
void 方法永远不可用 when(...).thenReturn(...) ——这是 Mockito 的根本设计约束。必须改用 doXxx() 链式调用:
// ✅ 正确:针对 final void 方法,启用 inline 后可成功 stub
doNothing()
.when(mockStatement)
.setStructured(eq(1), anyString(), any(SQLServerDataTable.class));
// ❌ 错误:即使方法是 void,也绝不可用 when() 起头
// when(mockStatement.setStructured(...)).thenReturn(...); // 编译失败 + 运行时异常
3. 完整可运行示例(含验证)
@ExtendWith(MockitoExtension.class)
class SQLServerPreparedStatementTest {
@Mock
private Connection mockConnection;
@Mock
private SQLServerPreparedStatement mockStatement;
@Test
void testSetStructuredIsStubbed() throws SQLException {
// Arrange
Mockito.lenient().when(mockConnection.prepareStatement(anyString()))
.thenReturn(mockStatement);
// ✅ 关键:启用 inline 后,final void 方法可被 doNothing() stub
doNothing()
.when(mockStatement)
.setStructured(eq(1), anyString(), any(SQLServerDataTable.class));
// Act
try (var stmt = mockConnection.prepareStatement("SELECT 1")) {
// 触发 setStructured 调用(实际业务中由框架或代码调用)
((SQLServerPreparedStatement) stmt).setStructured(
1, "MyType", new SQLServerDataTable() // 实际构造略
);
}
// Assert:验证是否按预期调用(参数可选校验)
verify(mockStatement, times(1))
.setStructured(eq(1), anyString(), any(SQLServerDataTable.class));
}
}
? 补充说明与最佳实践
-
doNothing()是默认行为,但显式声明更清晰:对于finalvoid 方法,doNothing()不仅是占位,更是启用 stub 的必要声明;省略它将导致调用真实final方法(可能抛异常或连接数据库)。 -
参数匹配建议用
eq()/any()而非isA():isA(Class)在泛型场景下易引发类型擦除问题,any(SQLServerDataTable.class)更健壮。 -
避免过度 stub:若仅需验证调用而非干预行为,
verify()已足够;仅当需模拟异常、捕获参数或触发副作用时,才需doAnswer()或doThrow()。 -
替代方案考虑:若无法修改测试环境(如禁用 inline),可改用
@Spy+doCallRealMethod()组合,或通过包装器模式解耦对final类的直接依赖。
? 总结:
"Only void methods can doNothing()"是 Mockito 的“安全熔断”提示,本质是告诉你——这个 void 方法太“硬”(final),普通 mock 失效。启用mock-maker-inline,切换至doXxx()语法,即可精准掌控 final void 方法的行为,让测试既可靠又可控。










