You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cucumber并行执行时Spring Boot中WireMock存根定义失败

Spring Boot+Cucumber+WireMock并行测试失败问题排查与解决

问题背景

我有一个Spring Boot应用,使用Cucumber和WireMock做集成测试。单线程执行时测试完全正常,但开启cucumber.execution.parallel.enabled=true切换到并行执行后,测试开始失败,抛出如下JSON解析异常:

com.github.tomakehurst.wiremock.common.JsonException: 
{
  "errors" : [ {
    "code" : 10,
    "source" : {
      "pointer" : "/code"
    },
    "title" : "Error parsing JSON",
    "detail" : "Unrecognized field \"code\" (class com.github.tomakehurst.wiremock.common.Errors), not marked as ignorable"
  } ]
}
    at com.github.tomakehurst.wiremock.common.JsonException.fromJackson(JsonException.java:53)
    at com.github.tomakehurst.wiremock.common.Json.read(Json.java:60)
    at com.github.tomakehurst.wiremock.client.HttpAdminClient.safelyExecuteRequest(HttpAdminClient.java:519)
    at com.github.tomakehurst.wiremock.client.HttpAdminClient.executeRequest(HttpAdminClient.java:489)
    at com.github.tomakehurst.wiremock.client.HttpAdminClient.executeRequest(HttpAdminClient.java:466)
    at com.github.tomakehurst.wiremock.client.HttpAdminClient.addStubMapping(HttpAdminClient.java:146)
    at com.github.tomakehurst.wiremock.client.WireMock.register(WireMock.java:414)
    at com.github.tomakehurst.wiremock.client.WireMock.register(WireMock.java:409)
    at com.github.tomakehurst.wiremock.client.WireMock.givenThat(WireMock.java:115)
    at com.github.tomakehurst.wiremock.client.WireMock.stubFor(WireMock.java:119)

请问WireMock.stubFor是否为线程安全操作?从异常来看似乎不是。如果它是非线程安全的,有哪些可行的解决方案(比如同步/锁定所有stubFor的执行)?

存根初始化示例

WireMock.stubFor(post(urlPathMatching("/test"))
    .withRequestBody(equalToJson(..))
    .willReturn(aResponse()
        .withStatus(200)
        .withHeader("Content-Type", "application/json")
        .withBody(..)));

WireMock创建方式

WireMockServer wireMock = new WireMockServer(options()
    .port(8000)
    .extensions(new ResponseTemplateTransformer(true))
    .notifier(new ConsoleNotifier(true)));
wireMock.start();
configureFor(8000);

版本信息:com.github.tomakehurst:wiremock-jre8-standalone:2.33.0


问题分析

WireMock的stubFor操作本身不是线程安全的。当多个测试线程同时向同一个WireMock实例注册存根时,会触发内部并发冲突,导致WireMock的JSON处理逻辑混乱,进而抛出字段无法识别的解析异常。

可行解决方案

1. 全局同步锁控制存根注册

在所有调用WireMock.stubFor的代码外层,使用同一个全局锁对象做同步,确保同一时间只有一个线程在注册存根:

private static final Object WIREMOCK_STUB_LOCK = new Object();

// 注册存根时包裹同步块
synchronized (WIREMOCK_STUB_LOCK) {
    WireMock.stubFor(post(urlPathMatching("/test"))
        .withRequestBody(equalToJson(..))
        .willReturn(aResponse()
            .withStatus(200)
            .withHeader("Content-Type", "application/json")
            .withBody(..)));
}

这种方式实现简单,但会让存根注册阶段变为串行,牺牲部分并行性能。

2. 为每个测试线程分配独立WireMock实例

避免多线程共享同一个WireMock服务,给每个测试线程启动独立的实例并使用随机端口:

// 每个线程启动独立的WireMockServer,用随机端口避免冲突
WireMockServer wireMock = new WireMockServer(options()
    .dynamicPort()
    .extensions(new ResponseTemplateTransformer(true))
    .notifier(new ConsoleNotifier(true)));
wireMock.start();
// 配置当前线程使用该实例的端口
configureFor(wireMock.port());

测试结束后要记得停止当前线程的WireMock实例,避免资源泄漏。这种方式能最大化并行性能,因为每个线程的测试环境完全隔离,不会有存根冲突。

3. 批量注册存根

将多个存根提前整理为列表,一次性批量注册,减少并发操作的次数:

List<StubMapping> stubMappings = Arrays.asList(
    post(urlPathMatching("/test"))
        .withRequestBody(equalToJson(..))
        .willReturn(aResponse().withStatus(200))
        .build(),
    // 其他存根定义...
);

// 批量注册,减少同步块执行次数
synchronized (WIREMOCK_STUB_LOCK) {
    wireMock.addStubMappings(stubMappings);
}

这种方式比单个存根逐个注册的性能更好,能降低同步锁的开销。

4. 升级WireMock版本

你当前使用的2.33.0版本较旧,后续的WireMock 3.x系列对并发场景做了优化,修复了部分线程安全问题。如果项目允许升级依赖,可以尝试切换到较新的稳定版本,可能直接解决问题。


内容的提问来源于stack exchange,提问作者Vasyl Sarzhynskyi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 07:20:13