测试反应式扩展 - 如何将测试调度程序与 ToTask() 一起使用



我在测试使用基于任务的服务的反应式代码时遇到问题。 在我被测试的类中,我使用任务并使用ToObservable来做反应性的事情。

public void Method()
{
  _svc.MyTaskServiceMethod().ToObservable().Select(....) //pipe it elsewhere and do interesting things.
}

现在在单元测试中,我正在测试一些时间(使用最小起订量进行服务)

svcMock.Setup(x => x.MyTaskServiceMethod()).Returns(() =>
  Observable.Return("VALUE", testScheduler)
    .Delay(TimeSpan.FromMilliseconds(100), testScheduler)
    .ToTask()
  );

问题在于,尽管在返回/延迟调用中使用了测试计划程序,但任务本身仍在单独的线程上完成。 我通过在代码中添加当前托管线程 ID 的几个控制台写入来看到这一点。

svcMock.Setup(x => x.MyServiceMethod()).Returns(() =>
{
  var task = Observable.Return("VALUE", testScheduler)
   .Delay(TimeSpan.FromMilliseconds(1000), testScheduler)
   .Do(x => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + " Obs"); })
   .ToTask();
   task.ContinueWith((_) =>
   {
       Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + " Task");
   });
   return task;
});

Do(..) 在主测试线程上执行,并且在 testSchduler.AdvanceBy(..) 调用后完全按照我的预期发生。

任务延续仍在单独的线程中进行,并且基本上直到单元测试的主体完成之后才会执行。 因此,在我的目标正文中,没有什么能真正完成我的任务。ToObservable() observable。

默认情况下,

任务延续将使用任务池线程,因此您的延续将逃脱测试计划程序的控制。如果指定选项 TaskContinuationOptions.ExecuteSynchronously ,它将使用相同的线程,结果将根据需要发布到可观察量:

task.ContinueWith((_) =>
{
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + " Task");
}, TaskContinuationOptions.ExecuteSynchronously);

补遗

您可能会发现 Rx 站点上的这个相关讨论非常有启发性地介绍了 TPL 中的并发性 -> Rx 转换,尤其是ToObservable()

前段时间,我与人合著了一个基于 NUnit 的单元测试库,以帮助精确地进行 Rx 和 TPL 测试。为此,我们构建了一个测试 TPL 调度器来强制所有 TPL 任务在没有并发的情况下运行。您可以在此处查看相关代码:https://github.com/Testeroids/Testeroids/blob/master/solution/src/app/Testeroids/TplTestPlatformHelper.cs#L87

相关内容

  • 没有找到相关文章

最新更新