延迟后评估 NUnit 断言

Evaluate NUnit assertion after a delay

我正在尝试使用 NUnit 为一种可能需要 1 到 3 秒才能完成的方法编写单元测试。为了验证测试,我需要做的就是检查 List<string> entries 是否在 1 到 3 秒的跨度内递增。

我目前的解决方案是使用 Thread.Sleep():

1. int currentEntries = entries.count;
2. Call methodA()
3. Thread.Sleep(3000);
4. int updatedEntries = entries.count;
5. Assert.That(updatedEntries, Is.EqualTo(currentEntries+1));

此解决方案至少需要 3 秒,即使 methodA() 完成得更快也是如此。

我试过使用 NUnits 延迟约束:

Assert.That(entries.Count, Is.EqualTo(currentEntries+1).After(3).Seconds.PollEvery(250).MilliSeconds);

这将是理想的,因为它支持轮询。但是 After 约束仍然立即评估,而不是像 here.

所讨论的那样在 3 秒后评估

这个问题有更好的解决方案吗?

查理的回答解决了您的实际问题(他的回答总是如此)。但是,我强烈建议不要进行依赖于延迟的测试。如果您的测试失败,您如何知道问题是否是您等待的时间不够长。

此外,如果这是自动化测试套件的一部分,那么随着您添加更多这样的测试,执行所有测试所花费的时间将变得非常昂贵。

好的测试是确定性的。例如,请参阅 Martin Fowler 的 Eradicating Non-Determinism in Tests,其中包括短语:

Never use bare sleeps to wait for asynchonous responses: use a callback or polling.

我的建议是重构您的代码,以便将功能与线程分离,并测试功能,而不是线程部分。

一旦实现了这种分离,就可以为调用线程上的功能的代码提供一个 Mock,您可以简单地验证 Mock 是否被调用,因为您已经单独测试了它的行为是否正确,尽管仍然需要轮询...所以:

如果您封装启动单独线程的代码,那么在您的测试中,提供一个实际同步执行代码的模拟线程启动器。因此,一旦被测试的代码完成,您就可以进行断言,无需等待。

您对 Assert.That 的调用有一个实际参数,它会在调用方法之前立即计算。取 entries.Count 的值并将生成的整数值复制为方法参数。在该方法中,我们正在处理一个常量。

当约束每 250 毫秒重新计算一次时,每次都是针对相同的复制常量完成的,当然永远不会改变。

当您使用延迟约束时,无论有无轮询,实际参数必须采用委托、lambda 或字段引用的形式。下面的简单修改应该可以让它工作。

Assert.That (() => entries.Count, Is.EqualTo(currentEntries+1).After(3).Seconds.PollEvery(250).MilliSeconds);

或者,这也应该有效

Assert.That (entries, Has.Count.EqualTo(currentEntries+1).After(3).Seconds.PollEvery(250).Milliseconds);

因为每次轮询时都会评估 属性。