C# 由于计时和线程问题,NUnit测试执行失败

C# 由于计时和线程问题,NUnit测试执行失败,c#,nunit,task-parallel-library,gitlab-ci,github-actions,C#,Nunit,Task Parallel Library,Gitlab Ci,Github Actions,我们在.NET应用程序中有很多测试。有些测试与时间相关,有些代码与.NET TPL是多线程的。几个星期以来,我们和他们有很多问题。在此之前,每次测试都会成功运行 我举了一个简单计时器的例子: 在类方法ScheduleExecution(L107)中,将启动正常的System.Threading.Timer。相应的测试在ScheduleExecutionWithStop(L79)方法中,其中计时器将启动,测试线程将被阻塞一段时间,然后如果计时器已过,将对其进行评估 第三方物流的另一个例子: 公共类

我们在.NET应用程序中有很多测试。有些测试与时间相关,有些代码与.NET TPL是多线程的。几个星期以来,我们和他们有很多问题。在此之前,每次测试都会成功运行

我举了一个简单计时器的例子:

在类方法ScheduleExecution(L107)中,将启动正常的
System.Threading.Timer
。相应的测试在ScheduleExecutionWithStop(L79)方法中,其中计时器将启动,测试线程将被阻塞一段时间,然后如果计时器已过,将对其进行评估

第三方物流的另一个例子:

公共类Foo
{
公共无效启动(整数延迟)
{
Task.Run(异步委托
{
等待任务。延迟(延迟);
TaskDone?.Invoke(this,EventArgs.Empty);
});
}
公共事件事件处理程序任务已完成;
}
[测试夹具]
公营足球场
{
[测试]
公共void TestRunner()
{
//安排
var foo=new foo();
var resetEvent=新的手动重置事件(false);
foo.TaskDone+=委托
{
resetEvent.Set();
};
//表演
foo.Start(1000);
var result=resetEvent.WaitOne(2000);
//断言
Assert.IsTrue(结果,“任务未及时完成”);
}
}
新任务将使用
任务启动。运行
。任务由于某些原因而延迟,然后引发事件。测试注册到事件并检查任务是否及时执行

如果使用使用NUnit测试运行程序的Visual Studio Resharper UnitTest Explorer执行测试,则测试大部分是成功的。如果将使用nunit控制台,则测试也将进行。这不仅在本地计算机上,在GitHub工作流运行程序和GitLab CI运行程序上也是如此。同样的问题,同样的结果

一个想法是改变等待计时器测试的时间。这是可行的,但为什么现在要这样做呢?试验成功了几个月/几年。此外,构建基础设施的速度越来越快,性能也越来越高

我将同样的想法应用到
任务中。运行
测试。但即使我将时间设置为60秒,它也不会运行。最令人困惑的是,测试在我的本地机器上运行良好,但在GitHub工作流运行程序和GitLab CI运行程序上运行不好

有没有人知道我能做些什么来解决这个问题,或者有没有更好的办法来测试这样的代码行

  • NUnit控制台:3.11.1
  • NUnit测试适配器:3.17.0

您可以尝试将测试重写为使用
TaskCompletionSource
CancellationToken

如果您不需要
EventArgs
,则重写的测试版本如下所示:

[测试]
公共异步任务TestRunner()
{
//安排
var foo=new foo();
var fooTaskCompleted=new TaskCompletionSource();
var timeoutPolicy=新的CancellationTokenSource(2000年);
timeoutPolicy.Token.Register((future)=>((TaskCompletionSource)future.trysetconceled(),useSynchronizationContext:false,state:fooTaskCompleted);
foo.TaskDone+=委托
{
fooTaskCompleted.SetResult(空);
};
//表演
foo.Start(1000);
等待任务完成;
//断言
Assert.IsFalse(fooTaskCompleted.Task.IsCanceled);
Assert.IsFalse(fooTaskCompleted.Task.IsFaulted);
}
如果您需要
EventArgs
,那么您可以像这样重写
TestRunner
方法:

[测试]
公共异步任务TestRunner()
{
//安排
var foo=new foo();
var fooTaskCompleted=new TaskCompletionSource();
var timeoutPolicy=新的CancellationTokenSource(2000年);
timeoutPolicy.Token.Register((future)=>((TaskCompletionSource)future.trysetconceled(),useSynchronizationContext:false,state:fooTaskCompleted);
foo.TaskDone+=(s,e)=>
{
Footask完成。试验结果(e);
};
//表演
foo.Start(1000);
var eventArgs=wait fooTaskCompleted.Task;
//断言
Assert.IsFalse(fooTaskCompleted.Task.IsCanceled);
Assert.IsFalse(fooTaskCompleted.Task.IsFaulted);
}

您是否尝试过从外部使用?而不是事件?我不知道该任务。Run在Foo中使用。当然,Start可以返回任务,我可以使用TCS,但这只是我从代码中提取的一个示例。哦,惊喜的黑盒子。。。好啊棘手的但问题不同:你真的需要这样才能在tMax上失败吗?或者你只是想确保在特定的环境中达到特定的基准,但允许“峰值”?对于计时器来说,峰值通常是可以的,但为什么它们变化这么大?在带有Task.Run的示例中,我可以将WaitOne的时间设置为小时,但不会设置。有点复杂,但我会尝试将其用于任务。运行case!谢谢-我会给你一个提示,如果它成功与否!嗨@dennis,有什么进展吗?是的,TaskCompletionSource周围的工作很有效。剩下的时间里,我真的不知道该怎么做。在我的其他一些项目中,测试使用的是更新版本的Nunit控制台运行程序。