4. Mock框架选型:Mockito、WireMock、Spring Cloud Contract对比与选择
在OAuth2.0 Mock依赖隔离与移植适配实战中,选择合适的Mock框架是决定测试效率与维护成本的关键。Mockito、WireMock、Spring Cloud Contract分别面向不同的Mock层次:对象级Mock、HTTP服务级Mock、契约级Mock。本章从OAuth2.0实战视角,对三者进行系统性对比,并给出选型建议。
4.1 三大Mock框架的核心定位
| 维度 | Mockito | WireMock | Spring Cloud Contract |
|---|---|---|---|
| Mock层次 | 对象/方法级 | HTTP服务级 | 契约/协议级 |
| 适用场景 | 单元测试、内部逻辑隔离 | 外部API模拟、集成测试 | 服务间契约验证、CDC |
| OAuth2.0典型用途 | Mock TokenService、UserDetailsService | Mock授权服务器、资源服务器端点 | 定义并验证授权码流程契约 |
| 启动方式 | 纯内存,无端口 | 独立HTTP服务器(可内嵌) | 插件生成测试桩,集成测试框架 |
| 验证能力 | 方法调用次数、参数、顺序 | 请求URL、Header、Body、延迟 | 请求/响应契约匹配 |
| 依赖隔离粒度 | 类/接口 | HTTP端点 | 服务间API契约 |
4.2 Mockito:对象级Mock的基石
Mockito是Java生态中最流行的单元测试Mock框架,通过动态代理或字节码增强,在内存中创建接口或类的Mock实例。在OAuth2.0测试中,Mockito主要用于隔离内部组件依赖。
OAuth2.0典型场景: Mock TokenStore、ClientDetailsService、UserApprovalHandler等内部Bean,验证授权码生成、Token刷新等核心逻辑。
// Mockito模拟TokenService,隔离数据库依赖
@ExtendWith(MockitoExtension.class)
class OAuth2TokenServiceTest {
@Mock
private TokenStore tokenStore;
@InjectMocks
private DefaultTokenService tokenService;
@Test
void testCreateAccessToken() {
OAuth2AccessToken mockToken = new DefaultOAuth2AccessToken("mock-access-token");
when(tokenStore.getAccessToken(any(OAuth2Authentication.class)))
.thenReturn(null);
when(tokenStore.createAccessToken(any(OAuth2Authentication.class)))
.thenReturn(mockToken);
OAuth2AccessToken result = tokenService.createAccessToken(
new OAuth2Authentication(null, null));
assertNotNull(result);
assertEquals("mock-access-token", result.getValue());
verify(tokenStore, times(1)).storeAccessToken(any(), any());
}
}
选型建议: 当测试目标聚焦于OAuth2.0业务逻辑本身(如Token生成策略、Scope校验规则),且不涉及外部HTTP调用时,优先选择Mockito。它轻量、快速,适合TDD驱动开发。
4.3 WireMock:HTTP服务级Mock的利器
WireMock通过启动一个轻量级HTTP服务器,精确模拟外部服务的REST API行为。在OAuth2.0集成测试中,WireMock可以完整模拟授权服务器、资源服务器或第三方身份提供商的端点。
OAuth2.0典型场景: 模拟授权服务器的 /oauth/authorize、/oauth/token 端点,模拟资源服务器的 /userinfo 端点,测试客户端在异常响应下的容错逻辑。
// WireMock模拟授权服务器Token端点
@WireMockTest(httpPort = 8089)
class OAuth2ClientIntegrationTest {
@Test
void testAuthorizationCodeFlow() {
// 模拟授权服务器返回Token响应
stubFor(post(urlEqualTo("/oauth/token"))
.withHeader("Authorization", containing("Basic"))
.withRequestBody(containing("authorization_code"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"access_token\":\"mock-at\",\"token_type\":\"Bearer\",\"expires_in\":3600}")));
// 模拟资源服务器验证Token
stubFor(get(urlEqualTo("/userinfo"))
.withHeader("Authorization", equalTo("Bearer mock-at"))
.willReturn(aResponse()
.withStatus(200)
.withBody("{\"sub\":\"user123\",\"name\":\"Test User\"}")));
// 执行OAuth2客户端流程
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(
new AuthorizationCodeResourceDetails());
restTemplate.getAccessToken(); // 触发Token请求
// 验证请求被正确发送
verify(postRequestedFor(urlEqualTo("/oauth/token"))
.withRequestBody(matchingJsonPath("$.grant_type")));
}
}
避坑提示: WireMock默认使用JVM端口,在并行测试时需注意端口冲突。建议为每个测试类分配独立端口,或使用动态端口分配(@WireMockTest(httpPort = 0))。
4.4 Spring Cloud Contract:契约级Mock的演进
Spring Cloud Contract(SCC)将Mock提升到契约层面:由服务提供方定义契约(Groovy DSL或YAML),自动生成WireMock桩和验收测试。在OAuth2.0微服务架构中,SCC确保授权服务与资源服务之间的API契约始终一致。
OAuth2.0典型场景: 定义授权服务器Token端点契约,资源服务器自动生成Mock桩,在消费者端验证Token校验逻辑。
// contracts/shouldReturnToken.groovy
Contract.make {
description "授权服务器返回Access Token"
request {
method POST()
url "/oauth/token"
headers {
contentType(applicationJson())
header("Authorization", "Basic Y2xpZW50OnNlY3JldA==")
}
body([
grant_type: "authorization_code",
code : "auth_code_123",
redirect_uri: "http://localhost:8080/callback"
])
}
response {
status 200
headers {
contentType(applicationJson())
}
body([
access_token : "contract-at-001",
token_type : "Bearer",
expires_in : 3600,
scope : "read write"
])
}
}
选型建议: 当团队采用微服务架构,且OAuth2.0服务由不同团队维护时,SCC是强制契约一致性的最佳选择。它天然支持消费者驱动的契约(CDC),避免因服务端接口变更导致客户端崩溃。
4.5 三框架对比总结与选型矩阵
4.6 组合使用策略:分层Mock架构
在大型OAuth2.0项目中,单一框架往往无法覆盖所有测试层次。推荐采用分层Mock架构,将三个框架组合使用:
- 单元测试层(Mockito):隔离Service、Repository、Validator等内部组件,验证OAuth2.0核心逻辑。
- 集成测试层(WireMock):模拟外部授权服务器、资源服务器、第三方IdP,验证客户端完整流程。
- 契约测试层(Spring Cloud Contract):定义并验证服务间API契约,确保提供方与消费方同步演进。
实战建议: 在OAuth2.0微服务项目中,建议以Spring Cloud Contract作为顶层契约管理,底层自动生成WireMock桩用于集成测试,而Mockito用于单元测试。这种分层策略既保证了契约一致性,又兼顾了测试速度与覆盖度。
4.7 选型速查表
| 决策条件 | 推荐框架 | 理由 |
|---|---|---|
| 测试目标为内部业务逻辑 | Mockito | 无需启动HTTP服务,执行速度快 |
| 需要模拟外部OAuth2.0端点 | WireMock | 精确控制请求/响应,支持故障注入 |
| 多团队协作,需强制契约一致 | Spring Cloud Contract | 契约即代码,自动生成桩与验证 |
| 需要测试Token刷新、过期重试 | WireMock + Mockito | WireMock模拟延迟/错误,Mockito验证重试逻辑 |
| 全链路契约验证(CDC) | Spring Cloud Contract | 提供方定义契约,消费方自动验证 |
| 遗留系统无契约文档 | WireMock | 通过录制/回放快速生成Mock |
重要提醒: 不要试图用一个框架解决所有Mock问题。Mockito无法模拟HTTP端点,WireMock无法验证方法调用次数,Spring Cloud Contract不适合单服务单元测试。理解每个框架的边界,才能构建高效、可维护的OAuth2.0测试体系。