如何在xUnit中延迟断言

问题描述

我有一种情况,我会在几秒钟后将元素添加到具有到期日期的清单中。 该程序在内部具有一个调度程序(内存中的计时器),该调度程序将自动更新该元素以将其设置为在达到到期日期时已到期。

如何使用xUnit测试这种情况以断言X秒钟后状态就是我期望的状态?

一次(并行)运行所有测试时遇到问题,因为尽管设置了线程睡眠或任务延迟,但断言一段时间后该元素仍然失败。

但是单独运行它会成功,我不确定幕后发生的事情。

这些是我的测试。

public static class ExpireElementTests
{
    public class Given_An_Inventory_And_An_AddElement_Command_With_Expiration_Shortly_When_Posting_It
        : IntegrationTest
    {
        private HttpResponseMessage _result = null!;
        private StringContent _dto = null!;
        private InventoryReadModel _expectedImmediateInventory = null!;
        private InventoryReadModel _expectedDelayedInventory = null!;

        protected override void Given()
        {
            var currentTime = DateTime.UtcNow;
            var addElement =
                new AddElement
                {
                    AggregateId = Constants.Inventory.Id,Name = "foo",ExpiresOn = currentTime.AddSeconds(1),Type = ElementType.Meat
                };
            _dto = addElement.ToStringContent();

            _expectedImmediateInventory =
                new InventoryReadModel
                {
                    InventoryId = Constants.Inventory.Id,Elements =
                        new List<ElementReadModel>
                        {
                            new ElementReadModel
                            {
                                Name = addElement.Name,Type = addElement.Type,ExpiresOn = addElement.ExpiresOn,IsExpired = false,IsRemoved = false
                            }
                        }
                };

            _expectedDelayedInventory =
                new InventoryReadModel
                {
                    InventoryId = Constants.Inventory.Id,IsExpired = true,IsRemoved = false
                            }
                        }
                };
        }

        protected override async Task WhenAsync()
        {
            _result =
                await HttpClient
                    .PostAsync(
                        "/commands/addElement",_dto);
        }

        [Fact]
        public void Then_It_Should_Return_Ok()
        {
            _result.StatusCode.Should().Be(HttpStatusCode.OK);
        }

        [Fact]
        public async Task Then_It_Should_Have_Created_The_Expected_Inventory_With_Element_Not_Expired()
        {
            var projection = AppContainer.Resolve<InMemoryInventoryProjection>();
            var inventory = await projection.GetInventory(Constants.Inventory.Id);
            inventory.Should().BeEquivalentTo(_expectedImmediateInventory);
        }

        // This tests fails only when running all at once
        [Fact]
        public async Task Then_It_Should_Have_The_Expected_Delayed_Inventory_After_The_Expiration_Date()
        {
            Thread.Sleep(4000);
            var projection = AppContainer.Resolve<InMemoryInventoryProjection>();
            var inventory = await projection.GetInventory(Constants.Inventory.Id);
            inventory.Should().BeEquivalentTo(_expectedDelayedInventory);
        }
    }
}

如果有意义,在到达到期日期时自动使库存中的元素到期的业务逻辑是一个简单的内存中计时器,该计时器在单例对象中运行,因此从理论上讲,实例是所有创建的时间第一次将DI容器注入内存后,它就会进入内存。

public Task ScheduleExpiration(Guid inventoryId,string elementName,DateTime expiresOn)
{
    var currentTicks = DateTime.UtcNow.Ticks;
    var expiresOnTicks = expiresOn.Ticks;
    var dueTimeMilliseconds = (expiresOnTicks - currentTicks) / TimeSpan.TicksPerMillisecond;
    _ =
        new Timer(
            state =>
            {
                var expireElement =
                    new ExpireElement
                    {
                        AggregateId = inventoryId,Name = elementName
                    };

                _expireElementHandler.Handle(expireElement);
            },null,dueTimeMilliseconds,0);
    return Task.CompletedTask;
}

更新1 :一种解决方法是在运行断言之前实际强制执行Task延迟。我不确定这是否是最佳实践,但是该测试仅在运行时通过,而不是在调试时通过。这里肯定有一些时髦的东西。

public class Given_An_Inventory_With_An_Element_About_To_Expire_When_Reaching_Expiration_Time 
    : IntegrationTest
{
    private InventoryReadModel _expectedInventory = null!;

    protected override void Given()
    {
        var currentTime = DateTime.UtcNow;
        var addElement =
            new AddElement
            {
                AggregateId = Constants.Inventory.Id,Type = ElementType.Meat
            };
        var dto = addElement.ToStringContent();
        _ =
            HttpClient
                .PostAsync(
                    "/commands/addElement",dto).GetAwaiter().GetResult();

        _expectedInventory =
            new InventoryReadModel
            {
                InventoryId = Constants.Inventory.Id,Elements =
                    new List<ElementReadModel>
                    {
                        new ElementReadModel
                        {
                            Name = addElement.Name,IsRemoved = false
                        }
                    }
            };
    }

    protected override async Task WhenAsync()
    {
        await Task.Delay(2000);
    }

    [Fact]
    public async Task Then_It_Should_Have_The_Expected_Inventory_With_Item_Expired()
    {
        var projection = AppContainer.Resolve<InMemoryInventoryProjection>();
        var inventory = await projection.GetInventory(Constants.Inventory.Id);
        inventory.Should().BeEquivalentTo(_expectedInventory);
    }
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...