集成测试Mock测试与依赖隔离安全权限配置实践 - 第2节

2.1 Mock对象定义

Mock对象是测试替身(Test Double)的一种,用于在单元测试或集成测试中模拟真实依赖的行为。它能够记录调用信息(如调用次数、参数、顺序),并根据预设的期望(Expectation)返回指定结果或抛出异常。

核心特征: Mock对象不仅提供“假实现”,还具备行为验证能力——测试结束后可以断言“某个方法是否被调用”“调用了多少次”“传入什么参数”。
特性说明
行为记录自动记录所有方法调用的参数、次数、顺序
预设返回值可配置当调用某方法时返回固定值或动态计算结果
异常模拟可配置在特定调用时抛出异常,测试异常处理路径
验证断言测试结束后可验证是否按预期调用了依赖

2.2 Mock与Stub/Fake的区别

三种测试替身常被混淆,但职责和复杂度有明显差异:

类型核心目的行为验证实现复杂度典型场景
Stub提供预设返回值,驱动被测对象走特定路径无低替代只读数据源、配置服务
Fake提供轻量级可工作实现(如内存数据库)无中替代数据库、消息队列
Mock验证交互行为是否符合预期有高验证外部服务调用、事件发布
记忆口诀: Stub问“返回什么”,Fake问“怎么工作”,Mock问“是否按预期调用”。

2.3 Mock框架工作原理

主流Mock框架(Mockito、EasyMock、PowerMock、jMock等)底层依赖两种核心技术:动态代理和字节码增强。

2.3.1 动态代理(Dynamic Proxy)

适用于接口或抽象类。框架在运行时生成一个实现目标接口的代理类,所有方法调用被拦截到InvocationHandler,由框架决定返回预设值还是记录调用。

// Mockito 动态代理示例(接口)
public interface UserService {
    User findById(Long id);
}

// 测试中
UserService mock = Mockito.mock(UserService.class);
Mockito.when(mock.findById(1L)).thenReturn(new User("Alice"));
mock.findById(1L);
Mockito.verify(mock).findById(1L);  // 验证调用

2.3.2 字节码增强(Bytecode Enhancement)

适用于具体类、final类、静态方法或私有方法。框架(如PowerMock、Mockito inline、ByteBuddy)在类加载时或运行时修改字节码,插入拦截逻辑。

// PowerMock 字节码增强示例(具体类)
public class Logger {
    public final void log(String msg) { /* 真实实现 */ }
}

// 测试中
Logger mock = PowerMock.createMock(Logger.class);
PowerMock.expectLastCall().andThrow(new RuntimeException("模拟异常"));
PowerMock.replay(mock);
mock.log("test");
PowerMock.verify(mock);
性能与限制: 字节码增强比动态代理开销更大,且可能被某些安全策略(如SecurityManager)阻止。在集成测试中优先使用动态代理,仅在无法Mock final/static方法时使用字节码增强。
Mock框架工作原理对比 动态代理(JDK Proxy) 适用于接口 / 抽象类 运行时生成代理类 InvocationHandler 拦截调用 性能好,无额外依赖 字节码增强(ByteBuddy / CGLIB) 适用于具体类 / final / static 类加载时或运行时修改字节码 子类化 / 重写方法插入拦截 功能强大,性能开销较大 选择依据:被测依赖是接口 → 动态代理;是具体类/final/static → 字节码增强 集成测试中优先使用动态代理,仅在必要时启用字节码增强

2.4 集成测试中的Mock选择策略

在集成测试中,Mock并非万能。以下决策树可帮助选择正确的替身类型:

  1. 依赖是否可控? 是 → 使用真实实例或Fake;否 → 进入下一步
  2. 是否需要验证交互行为? 是 → 使用Mock;否 → 使用Stub
  3. 依赖是接口还是具体类? 接口 → 动态代理Mock;具体类 → 字节码增强Mock
  4. 是否涉及静态/私有方法? 是 → 仅PowerMock/ByteBuddy;否 → Mockito inline即可
安全权限提示: 在集成测试中Mock外部服务(如认证中心、权限网关)时,务必确保Mock对象不会绕过安全上下文。建议在Mock中显式注入安全令牌或权限上下文,避免测试环境与生产环境的安全策略不一致。