c# - 由于计时和线程问题,NUnit 测试执行失败
问题描述
我们在 .NET 应用程序中进行了大量测试。其中一些测试与时间相关,一些代码使用 .NET TPL 进行多线程处理。几个星期以来,我们遇到了很多问题。以前,测试每次都运行成功。
我举了一个简单的计时器示例:
在类NotSoParallelOps方法 ScheduleExecution (L107)System.Threading.Timer
中将启动一个正常的。相应的测试在NotSoParallelOpsTests Method ScheduleExecutionWithStop (L79) 中,计时器将在其中启动,测试线程被阻塞一段时间,然后在计时器到时对其进行评估。
TPL 的另一个例子:
public class Foo
{
public void Start(int delay)
{
Task.Run(async delegate
{
await Task.Delay(delay);
TaskDone?.Invoke(this, EventArgs.Empty);
});
}
public event EventHandler TaskDone;
}
[TestFixture]
public class FooTests
{
[Test]
public void TestRunner()
{
// Arrange
var foo = new Foo();
var resetEvent = new ManualResetEvent(false);
foo.TaskDone += delegate
{
resetEvent.Set();
};
// Act
foo.Start(1000);
var result = resetEvent.WaitOne(2000);
// Assert
Assert.IsTrue(result, "Task not done in time");
}
}
一个新的任务将用 开始Task.Run
。该任务由于某些原因被延迟,然后引发了一个事件。测试注册到事件并检查任务是否及时执行。
如果Visual Studio Resharper UnitTest Explorer
使用 NUnit 测试运行程序执行测试,则测试大多是成功的。如果将使用 nunit-console,则测试经常失败,但有时也会成功。不仅在本地机器上也是如此,在 GitHub Workflow 运行器和 GitLab CI 运行器上也是如此。同样的问题,同样的结果。
一个想法是更改等待计时器测试的时间。它有效,但为什么现在要这样做?测试成功了几个月/几年。此外,构建基础架构变得更快、性能更高。
我将相同的想法应用于Task.Run
测试。但即使我将时间设置为 60 秒,它也不会运行。最令人困惑的是,测试在我的本地机器上运行良好,但在 GitHub Workflow 运行器和 GitLab CI 运行器上运行良好。
有谁知道我可以做些什么来解决这个问题,或者有没有更好的想法来测试这些代码行?
- NUnit 控制台:3.11.1
- NUnit 测试适配器:3.17.0
解决方案
您可以尝试重写您的测试以TaskCompletionSource
使用CancellationToken
.
如果您不需要,EventArgs
那么测试的重写版本将如下所示:
[Test]
public async Task TestRunner()
{
// Arrange
var foo = new Foo();
var fooTaskCompleted = new TaskCompletionSource<object>();
var timeoutPolicy = new CancellationTokenSource(2000);
timeoutPolicy.Token.Register((future) => ((TaskCompletionSource<object>)future).TrySetCanceled(), useSynchronizationContext: false, state: fooTaskCompleted);
foo.TaskDone += delegate
{
fooTaskCompleted.SetResult(null);
};
// Act
foo.Start(1000);
await fooTaskCompleted.Task;
// Assert
Assert.IsFalse(fooTaskCompleted.Task.IsCanceled);
Assert.IsFalse(fooTaskCompleted.Task.IsFaulted);
}
如果你需要,EventArgs
那么你可以像这样重写TestRunner
方法:
[Test]
public async Task TestRunner()
{
// Arrange
var foo = new Foo();
var fooTaskCompleted = new TaskCompletionSource<EventArgs>();
var timeoutPolicy = new CancellationTokenSource(2000);
timeoutPolicy.Token.Register((future) => ((TaskCompletionSource<EventArgs>)future).TrySetCanceled(), useSynchronizationContext: false, state: fooTaskCompleted);
foo.TaskDone += (s, e) =>
{
fooTaskCompleted.TrySetResult(e);
};
// Act
foo.Start(1000);
var eventArgs = await fooTaskCompleted.Task;
// Assert
Assert.IsFalse(fooTaskCompleted.Task.IsCanceled);
Assert.IsFalse(fooTaskCompleted.Task.IsFaulted);
}
推荐阅读
- android - 如何实现从回收器视图中删除元素的动画
- r - 如何在 R 中绘制或可视化多变量 D-vine copula
- ios - 只有 UNLocationNotificationTrigger 没有被触发
- python - 问答+NLP中的问题生成
- django - Django 的“精确”字段查找是否执行不区分大小写的搜索?
- python - 如何从文件中获取元素并将它们读入python中的列表
- sql - SQL中的匹配模式
- python - Kivy:在不活动或按下按钮时返回主屏幕?
- excel - VLOOKUP 在两个不同的工作簿之间无法正常工作
- laravel - 服务器将逗号识别为字符串