NUnit测试中Hangfire定时任务调度异常:首次通过后recurringJobDtos始终为空
解决NUnit测试中Hangfire二次执行
recurringJobDtos.Any()返回false的问题 问题根源
核心问题出在**JobStorage.Current是静态单例**:
- 首次测试初始化后,这个静态属性会指向第一次创建的InMemory存储实例;
- 当测试结束、Host被释放后,该存储实例已失效,但
JobStorage.Current并未被重置; - 第二次测试启动新Host并配置新的InMemory存储时,静态的
JobStorage.Current依然指向旧的失效实例,导致后续调用GetRecurringJobs()无法获取到任何有效数据。
另外原测试代码的循环没有延迟,会导致CPU占用过高,也可能加剧状态不一致的问题。
解决方案
1. 显式重置静态JobStorage实例
在每个测试结束后清理静态资源,确保下一次测试从零开始:
[TearDown] public void Cleanup() { // 重置静态JobStorage,避免跨测试污染 JobStorage.Current = null; }
同时在配置Hangfire时,显式将新创建的存储实例赋值给JobStorage.Current,确保指向正确:
services.AddHangfire(configuration => { var inMemoryStorage = new InMemoryStorage(); configuration .SetDataCompatibilityLevel(CompatibilityLevel.Version_170) .UseDefaultTypeSerializer() .UseStorage(inMemoryStorage); // 绑定当前测试的存储实例到静态属性 JobStorage.Current = inMemoryStorage; }) .AddHangfireServer();
2. 从依赖注入容器获取JobStorage(推荐)
避免直接使用静态JobStorage.Current,而是从当前Host的服务容器中获取实例,确保每次拿到的都是当前测试的有效存储:
// 从服务容器获取当前测试对应的JobStorage var jobStorage = webHost.Services.GetRequiredService<JobStorage>(); var b = false; do { var recurringJobDtos = jobStorage.GetConnection().GetRecurringJobs(); if (recurringJobDtos.Any()) { b = recurringJobDtos.First().LastJobState.StartsWith("Suc"); } // 添加延迟,降低CPU占用并给Job执行留足时间 await Task.Delay(100); } while (!b);
完整修改后的测试代码
using Hangfire; using Hangfire.Server; using Hangfire.Storage; using Hangfire.InMemory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using NUnit.Framework; namespace ClassLibrary1 { public class MyClass { [TearDown] public void Cleanup() { JobStorage.Current = null; } [TestCase(1)] [TestCase(2)] [Test] [Order(0)] public async Task Test(int not_used) { var defaultBuilder = Host.CreateDefaultBuilder(); var webHost = defaultBuilder .ConfigureServices(services => { services.AddHangfire(configuration => { var inMemoryStorage = new InMemoryStorage(); configuration .SetDataCompatibilityLevel(CompatibilityLevel.Version_170) .UseDefaultTypeSerializer() .UseStorage(inMemoryStorage); JobStorage.Current = inMemoryStorage; }) .AddHangfireServer(); }) .Build(); await webHost.StartAsync(); var jobId = TestContext.CurrentContext.Test.Name; RecurringJob.AddOrUpdate(jobId, () => new TestJobDI().TestJobId(null), Cron.Never); RecurringJob.Trigger(jobId); var jobStorage = webHost.Services.GetRequiredService<JobStorage>(); var b = false; do { var recurringJobDtos = jobStorage.GetConnection().GetRecurringJobs(); if (recurringJobDtos.Any()) { b = recurringJobDtos.First().LastJobState.StartsWith("Suc"); } await Task.Delay(100); } while (!b); await webHost.StopAsync(); webHost.Dispose(); } } public class TestJobDI { public void TestJobId(PerformContext context) { } } }
内容的提问来源于stack exchange,提问作者Apostolis Bekiaris
相关产品推荐
相关产品推荐

