如何在Xunit中进行Quartz集成测试?Outbox消息未处理
问题:Xunit测试中Quartz作业不处理Outbox消息
我需要测试一个Quartz作业,该作业负责触发领域事件以修改用户余额,手动运行时一切正常。但在Xunit测试中,该作业完全不处理Outbox消息,ProcessedOnUtc始终为null。
测试代码
[Fact] public async Task Should_WinAuction_When_BuyoutPrice() { var user = User.Create(Guid.NewGuid(), "example", "example@.com"); var userTwo = User.Create(Guid.NewGuid(), "example2", "example2@.com"); var oldBalance = user.Balance; _userRepository.Add(user); _userRepository.Add(userTwo); await UnitOfWork.SaveChangesAsync(); var auction = Auction.Create(Guid.NewGuid(), "example", "description", 10m, 11m, DateTime.Now, user.Id); _auctionRepository.Add(auction); await UnitOfWork.SaveChangesAsync(); var command = new PlaceBidCommand(11m, userTwo.Id, auction.Id); var result = await Sender.Send(command); Assert.True(result.IsSuccess); var bid = await _bidRepository.GetByIdAsync(result.Data.BidId); Assert.NotNull(bid); Assert.Equal(userTwo.Id, auction.WinnerId); await Task.Delay(5000); var updatedUser = DbContext.Users.FirstOrDefault(u => u.Id == userTwo.Id); Assert.True(updatedUser.Balance != 10000); }
Quartz注册代码
// Quartz 服务注册 services.AddQuartz(options => { var jobKey = JobKey.Create(nameof(ProcessOutboxMessages)); options.SchedulerId = Guid.NewGuid().ToString(); options.SchedulerName = "OutboxMessages"; options.AddJob<ProcessOutboxMessages>(jobKey) .AddTrigger(trigger => trigger.ForJob(jobKey).WithSimpleSchedule(schedule => { schedule.WithIntervalInSeconds(1); schedule.RepeatForever(); })); }); services.AddQuartzHostedService(options => { options.WaitForJobsToComplete = true; });
作业代码
public async Task Execute(IJobExecutionContext context) { var messages = await _dbContext.OutboxMessages .Where(x => x.ProcessedOnUtc == null) .Take(20) .ToListAsync(); foreach (var message in messages) { IDomainEvent? domainEvent = JsonConvert.DeserializeObject<IDomainEvent>(message.Content, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }); if (domainEvent == null) { continue; } await _publisher.Publish(domainEvent); message.ProcessedOnUtc = DateTime.UtcNow; } await _dbContext.SaveChangesAsync(); }
解决方案
1. 手动启动Quartz调度器
Xunit测试宿主不会自动启动后台服务,需要在测试中手动启动Quartz调度器:
private readonly IScheduler _scheduler; public YourTestClass(IScheduler scheduler) { _scheduler = scheduler; } [Fact] public async Task Should_WinAuction_When_BuyoutPrice() { await _scheduler.Start(); // 原测试代码逻辑... }
2. 确保DbContext实例一致
测试与作业使用的DbContext可能不是同一个实例,导致作业读取不到测试生成的消息:
- 检查DI配置,确保DbContext注册为
Scoped; - 在测试中通过同一个服务作用域获取DbContext,避免实例隔离。
3. 解决事务隔离问题
测试中保存消息的事务可能未提交,导致作业无法读取未提交数据:
- 确认
UnitOfWork.SaveChangesAsync()已提交事务; - 若使用事务性测试框架,确保作业执行前事务已完成提交。
4. 手动触发作业替代延迟等待
Task.Delay(5000)不可靠,可直接触发作业执行:
// 方式1:直接调用作业逻辑 var job = new ProcessOutboxMessages(_dbContext, _publisher); await job.Execute(null); // 方式2:通过调度器触发作业 await _scheduler.TriggerJob(JobKey.Create(nameof(ProcessOutboxMessages)));
5. 校验序列化配置
确保生成Outbox消息和作业反序列化时使用相同的JsonSerializerSettings,必须启用TypeNameHandling.All,否则无法正确识别具体领域事件类型。
内容的提问来源于stack exchange,提问作者Pentium3k
相关产品推荐
相关产品推荐

