
本文详解如何正确使用 mockito 对含默认方法的泛型接口(如 xyzconverter<d, e>)编写可靠、可维护的单元测试,重点规避“错误地 mock 被测目标”这一常见误区,并通过真实可运行示例展示 spy + mock 的协同用法。
本文详解如何正确使用 mockito 对含默认方法的泛型接口(如 xyzconverter<d, e>)编写可靠、可维护的单元测试,重点规避“错误地 mock 被测目标”这一常见误区,并通过真实可运行示例展示 spy + mock 的协同用法。
在 Java 单元测试中,对接口的默认方法进行测试时,一个关键原则是:默认方法本身是被测逻辑,不应被 mock;而它所依赖的其他抽象方法(如 createFrom)才应被模拟。直接对接口类型调用 spy(XYZConverter.class) 是非法的(接口无法实例化),且原代码中存在多处典型误用:spy 作用于接口类、泛型参数未绑定、@Mock 字段未正确注入、测试方法签名不合法等。
✅ 正确做法是:
- 使用 @Spy 注解一个 具体实现类的实例(或利用 Mockito.spy() 创建部分模拟的代理);
- 若无实现类,可借助 Lambda 或匿名内部类构造一个轻量实现,再对其 spy;
- 仅 mock 接口中的被默认方法调用的抽象方法(如 createFrom(D)),确保默认方法逻辑(createFromDtos)能真实执行;
- 泛型类型需在测试类或字段声明中明确绑定(如 <DomainClass, EntityClass>),避免原始类型导致的类型擦除问题。
以下为可直接运行的完整测试示例(基于 JUnit 4 + Mockito 3+):
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class XYZConverterTest {
// ✅ 正确:Spy 一个匿名实现(满足泛型约束),使默认方法可执行
@Spy
XYZConverter<DomainClass, EntityClass> converter = new XYZConverter<DomainClass, EntityClass>() {
@Override
public EntityClass createFrom(DomainClass dto) {
return null; // 实际由 mock 控制
}
@Override
public EntityClass updateEntity(EntityClass entity, DomainClass dto) {
return null;
}
};
@Mock
DomainClass domainClass;
@Mock
EntityClass entityClass;
@Test
public void createFromDtos() {
// ✅ 正确:只 mock 被默认方法调用的抽象方法
when(converter.createFrom(domainClass)).thenReturn(entityClass);
// 执行默认方法(真实逻辑)
List<EntityClass> result = converter.createFromDtos(Arrays.asList(domainClass));
// ✅ 验证结果符合预期
assertEquals(List.of(entityClass), result);
}
}⚠️ 注意事项:
- 不要 mock 默认方法本身:when(converter.createFromDtos(...)) 是无效且反模式的——这会绕过你要验证的核心逻辑。
- 泛型必须显式指定:XYZConverter<DomainClass, EntityClass> 不可写作裸 XYZConverter,否则编译失败或类型不安全。
- @Spy 要求目标可实例化:接口需通过匿名类或实现类构造实例;若使用 JUnit 5,推荐改用 MockitoExtension 并手动 spy(new Impl())。
- 确保依赖方法已覆盖:createFromDtos 内部调用 this::createFrom,因此 createFrom 必须被 when(...).thenReturn(...) 显式 stub,否则将触发 NullPointerException 或空返回。
总结:测试泛型接口的默认方法,本质是「测试组合逻辑」——默认方法是 glue code,其行为取决于所委托的抽象方法。因此,测试策略应聚焦于:Spy 接口实现 → Stub 其依赖的抽象方法 → 验证默认方法的组合输出。这一思路同样适用于 Spring Data Repository 默认方法、Java 8+ Collection 接口扩展方法等场景。

















