微信 API Mock Server 的 WireMock 规则编写与契约测试集成

在开发对接企业微信或微信支付等服务时,频繁调用真实 API 会受限于配额、网络延迟及环境依赖。使用 WireMock 构建本地 Mock Server 可有效解耦外部依赖,提升单元测试稳定性与执行速度。本文展示如何基于 WireMock 编写微信 API 的 Mock 规则,并将其集成到基于 Spring Boot 的契约测试流程中,确保接口契约一致性。

WireMock 基础配置与启动

首先引入依赖(pom.xml):

<dependency>
    <groupId>com.github.tomakehurst</groupId>
    <artifactId>wiremock-jre8</artifactId>
    <version>2.35.0</version>
    <scope>test</scope>
</dependency>

在测试类中启动 WireMock 服务,模拟微信域名 qyapi.weixin.qq.com

package wlkankan.cn.test.mock;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;

public abstract class BaseWeComMockTest {

    protected WireMockServer wireMockServer;

    @BeforeEach
    void startMockServer() {
        wireMockServer = new WireMockServer(WireMockConfiguration.options()
            .port(8089)
            .bindAddress("127.0.0.1")
            .httpsPort(8443)
            .usingFilesUnderClasspath("wiremock/wechat"));
        wireMockServer.start();
        // 动态替换微信 API 地址为 localhost:8089
        System.setProperty("wecom.api.base-url", "http://localhost:8089");
    }

    @AfterEach
    void stopMockServer() {
        if (wireMockServer != null) {
            wireMockServer.stop();
        }
    }
}

在这里插入图片描述

编写微信获取 access_token 的 Mock 规则

微信获取 token 的请求为:

POST https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET

src/test/resources/wiremock/wechat/mappings/gettoken.json 中定义响应:

{
  "request": {
    "method": "GET",
    "urlPath": "/cgi-bin/gettoken",
    "queryParameters": {
      "corpid": { "equalTo": "wx1234567890abcdef" },
      "corpsecret": { "equalTo": "secret_9876543210fedcba" }
    }
  },
  "response": {
    "status": 200,
    "headers": {
      "Content-Type": "application/json; charset=utf-8"
    },
    "jsonBody": {
      "errcode": 0,
      "errmsg": "ok",
      "access_token": "mock_access_token_abc123",
      "expires_in": 7200
    }
  }
}

也可通过 Java API 动态注册:

package wlkankan.cn.test.mock;

import com.github.tomakehurst.wiremock.client.WireMock;
import static com.github.tomakehurst.wiremock.client.WireMock.*;

public class WeComTokenMockRule {

    public static void stubGetTokenSuccess(WireMockServer server) {
        server.stubFor(get(urlPathEqualTo("/cgi-bin/gettoken"))
            .withQueryParam("corpid", equalTo("wx1234567890abcdef"))
            .withQueryParam("corpsecret", equalTo("secret_9876543210fedcba"))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json; charset=utf-8")
                .withJsonBody(org.skyscreamer.jsonassert.JSONParser.parseJSON(
                    "{ \"errcode\": 0, \"errmsg\": \"ok\", \"access_token\": \"mock_token_xyz\", \"expires_in\": 7200 }"
                ))
            )
        );
    }
}

Mock 外部联系人列表接口

微信获取外部联系人列表的请求:

GET https://qyapi.weixin.qq.com/cgi-bin/externalcontact/list?access_token=TOKEN&userid=USERID

Java 动态 Stub 示例:

package wlkankan.cn.test.mock;

import com.github.tomakehurst.wiremock.WireMockServer;
import static com.github.tomakehurst.wiremock.client.WireMock.*;

public class WeComContactMockRule {

    public static void stubListExternalContacts(WireMockServer server) {
        server.stubFor(get(urlPathEqualTo("/cgi-bin/externalcontact/list"))
            .withQueryParam("access_token", equalTo("mock_token_xyz"))
            .withQueryParam("userid", equalTo("zhangsan"))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json; charset=utf-8")
                .withBodyFile("responses/externalcontact_list_success.json")
            )
        );
    }
}

对应的 src/test/resources/wiremock/wechat/__files/responses/externalcontact_list_success.json 内容:

{
  "errcode": 0,
  "errmsg": "ok",
  "external_userid": ["woAJ2GCAAAXtWyujaWJHDDGi0mACH71w", "woAJ2GCAAAXtWyujaWJHDDGi0mACH71x"]
}

集成到 Spring Boot 测试与契约验证

wlkankan.cn.service 包中编写服务类:

package wlkankan.cn.service;

import org.springframework.web.client.RestTemplate;

public class WeComContactService {

    private final RestTemplate restTemplate;
    private final String baseUrl;

    public WeComContactService(RestTemplate restTemplate, String baseUrl) {
        this.restTemplate = restTemplate;
        this.baseUrl = baseUrl;
    }

    public ExternalContactListResponse listContacts(String accessToken, String userId) {
        String url = baseUrl + "/cgi-bin/externalcontact/list?access_token=" + accessToken + "&userid=" + userId;
        return restTemplate.getForObject(url, ExternalContactListResponse.class);
    }
}

编写契约测试用例:

package wlkankan.cn.test.contract;

import wlkankan.cn.test.mock.BaseWeComMockTest;
import wlkankan.cn.test.mock.WeComTokenMockRule;
import wlkankan.cn.test.mock.WeComContactMockRule;
import wlkankan.cn.service.WeComContactService;
import org.junit.jupiter.api.Test;
import org.springframework.web.client.RestTemplate;

import static org.assertj.core.api.Assertions.assertThat;

public class WeComContactServiceContractTest extends BaseWeComMockTest {

    @Test
    void should_return_external_contact_list_when_token_valid() {
        // Given
        WeComTokenMockRule.stubGetTokenSuccess(wireMockServer);
        WeComContactMockRule.stubListExternalContacts(wireMockServer);

        WeComContactService service = new WeComContactService(new RestTemplate(), "http://localhost:8089");

        // When
        var response = service.listContacts("mock_token_xyz", "zhangsan");

        // Then
        assertThat(response.getErrcode()).isEqualTo(0);
        assertThat(response.getExternalUserid()).hasSize(2);
        assertThat(response.getExternalUserid().get(0))
            .isEqualTo("woAJ2GCAAAXtWyujaWJHDDGi0mACH71w");
    }
}

自动化契约生成与验证

可结合 Pact 或 Spring Cloud Contract 实现双向契约管理。但即使仅使用 WireMock,也可通过以下方式保障契约一致性:

  • .json 映射文件纳入版本控制;
  • 在 CI 流程中运行所有 Mock 测试;
  • 定期从真实微信 API 抓包更新响应样本。

通过 WireMock 精准模拟微信 API 行为,团队可在无网络、无权限环境下完成高覆盖率的集成测试,显著提升交付效率与系统健壮性。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐