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 三框架对比总结与选型矩阵

Mock框架选型决策树(OAuth2.0视角) Mock需求分析 内部逻辑隔离 外部HTTP服务模拟 服务间契约验证 Mockito WireMock Spring Cloud Contract OAuth2.0典型场景 Mock TokenStore Mock ClientDetailsService OAuth2.0典型场景 Mock /oauth/token 端点 Mock /userinfo 端点 OAuth2.0典型场景 定义Token端点契约 消费者驱动CDC验证 选型决策建议 • 单元测试/内部逻辑 → Mockito(轻量、快速、无端口依赖) • 集成测试/外部服务模拟 → WireMock(精确控制HTTP行为、支持延迟/故障注入) • 微服务契约/多团队协作 → Spring Cloud Contract(强制契约一致性、自动生成桩)

4.6 组合使用策略:分层Mock架构

在大型OAuth2.0项目中,单一框架往往无法覆盖所有测试层次。推荐采用分层Mock架构,将三个框架组合使用:

  1. 单元测试层(Mockito):隔离Service、Repository、Validator等内部组件,验证OAuth2.0核心逻辑。
  2. 集成测试层(WireMock):模拟外部授权服务器、资源服务器、第三方IdP,验证客户端完整流程。
  3. 契约测试层(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测试体系。