Specflow多场景集成测试共用Wiremock仅单测试通过问题咨询
根因分析
- 事件订阅时机滞后:
WaitForMessage方法仅订阅事件触发后产生的日志变更,未检查订阅前已经完成的请求日志。如果被测服务的请求在事件订阅逻辑执行前就已经发送到WireMock,就会无法触发等待信号,导致超时。 - 资源释放时序异常:你在向ScenarioContainer注册WireMock实例时已经开启了
dispose: true,容器会在场景结束时自动释放实例,同时你又在AfterScenario中手动调用Dispose,重复释放会导致端口资源回收延迟,后续场景的WireMock实例启动异常。 - 事件订阅未取消:
LogEntriesChanged的订阅没有手动取消,会导致旧WireMock实例被事件引用,无法被及时GC,进一步加剧端口占用问题。 - 若你的被测服务使用静态变量存储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
相关产品推荐
相关产品推荐

