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

Specflow多场景集成测试共用Wiremock仅单测试通过问题咨询

根因分析
  1. 事件订阅时机滞后:WaitForMessage 方法仅订阅事件触发后产生的日志变更,未检查订阅前已经完成的请求日志。如果被测服务的请求在事件订阅逻辑执行前就已经发送到WireMock,就会无法触发等待信号,导致超时。
  2. 资源释放时序异常:你在向ScenarioContainer注册WireMock实例时已经开启了dispose: true,容器会在场景结束时自动释放实例,同时你又在AfterScenario中手动调用Dispose,重复释放会导致端口资源回收延迟,后续场景的WireMock实例启动异常。
  3. 事件订阅未取消:LogEntriesChanged的订阅没有手动取消,会导致旧WireMock实例被事件引用,无法被及时GC,进一步加剧端口占用问题。
  4. 若你的被测服务使用静态变量存储WireMock地址,会导致后续场景的请求全部发往第一个场景的旧实例地址,自然无法匹配到日志。
修复方案

1. 优化日志等待逻辑

修改WaitForMessage方法,先检查已存在的日志,同时新增事件取消逻辑:

private void WaitForMessage(Predicate<ILogEntry> selector = null)
{
    var httpBackend = context.Get<WireMockServer>();
    using CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
    using ManualResetEventSlim waitHandle = new ManualResetEventSlim();

    // 先检查订阅前已产生的日志,避免漏匹配
    if (CheckExistingLogs(httpBackend, selector))
    {
        waitHandle.Set();
    }

    void OnLogEntriesChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (selector == null)
        {
            waitHandle.Set();
            return;
        }
        
        if (e.NewItems == null) return;
        foreach (ILogEntry item in e.NewItems)
        {
            if (selector(item))
            {
                waitHandle.Set();
                return;
            }
        }
    }

    httpBackend.LogEntriesChanged += OnLogEntriesChanged;
    try
    {
        waitHandle.Wait(cts.Token);
        cts.Token.ThrowIfCancellationRequested();
    }
    finally
    {
        // 手动取消订阅,避免引用残留
        httpBackend.LogEntriesChanged -= OnLogEntriesChanged;
    }
}

private bool CheckExistingLogs(WireMockServer server, Predicate<ILogEntry> selector)
{
    if (selector == null) 
        return server.LogEntries.Any();
    return server.LogEntries.Any(entry => selector(entry));
}

2. 修正资源释放逻辑

删除AfterScenario中手动释放WireMock的代码,由容器自动管理生命周期:

[AfterScenario]
public static void DisposeAll(ScenarioContext context)
{
    // 移除该行:context.Get<WireMockServer>().Dispose();
    // 其余清理逻辑保留
}

3. 优化WireMock启动配置

显式指定端口范围,避免端口占用冲突,同时限制日志留存避免残留:

private static WireMockServer RegisterWireMock(ScenarioContext scenarioContext)
{
    var httpBackend = WireMockServer.Start(new WireMockServerSettings()
    {
        UseSSL = false,
        // 指定随机端口范围,减少端口冲突概率
        Port = 9000 + Random.Shared.Next(0, 1000),
        // 自动清空历史日志
        RequestLogExpirationDuration = 1,
        MaxRequestLogCount = 0
    });
    scenarioContext.ScenarioContainer.RegisterInstanceAs(httpBackend, dispose: true);
    return httpBackend;
}

4. 检查被测服务配置

确认被测服务的WireMock调用地址没有用静态变量存储,每次场景启动时都从当前ScenarioContext中获取最新实例的地址。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 10:15:04