【问题标题】:Testing Reactive Extensions - How do I use the test scheduler with ToTask()?测试响应式扩展 - 如何将测试调度程序与 ToTask() 一起使用?
【发布时间】:2015-01-25 15:01:12
【问题描述】:

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

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

现在在单元测试中,我正在测试一些时间(使用 Moq 作为服务)

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

问题在于,尽管在 Return/Delay 调用中使用了测试调度程序,但任务本身仍在单独的线程上完成。我通过将当前托管线程 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(..) 调用之后发生。

任务延续仍在一个单独的线程中进行,并且基本上在单元测试主体完成之前不会执行。因此,在我的目标主体中,没有任何东西真正通过我的 task.ToObservable() observable。

【问题讨论】:

标签: unit-testing system.reactive


【解决方案1】:

默认情况下,任务延续将使用任务池线程,因此您的延续会逃脱测试调度程序的控制。如果您指定选项TaskContinuationOptions.ExecuteSynchronously,它将使用相同的线程,结果将根据需要发布到可观察对象:

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

附录

您可能会发现 this related discussion on the Rx site 对 TPL -> Rx 转换中的并发主题非常有启发性,尤其是 ToObservable()

【讨论】:

  • 这对于我的线程 ID 演示代码来说很好,但我真正关心的延续是嵌入在 ToObservable() 扩展方法中的。我很犹豫是否仅为这个测试场景推出我自己版本的该库方法
  • 为了清楚起见,我现在已经测试了这个想法,它确实给出了我正在寻找的结果。我从响应式扩展源代码中复制了 ToObservable 扩展并对其进行了更改以使用此延续选项。
  • 我想知道是否有办法强制任务同步运行其延续 - 基本上与这个问题相反:stackoverflow.com/questions/22579206
  • 我不知道有一种通用的干净/简单的方法来做到这一点。自定义 TaskScheduler 或指定任务选项显然是您不想要的“噪音”,而且通常不方便。在 TPL 和 Rx 之间移动通常有丑陋的细节。我知道并不总是一个选项,但我参与的一些项目已决定优先使用 Rx - 即使对于单一结果操作 - 正是为了避免这些缺陷并增强可测试性......
  • ...鉴于 TPL 代码在 BCL 和 3rd 方库中的流行,以及 TPL 被编码人员更广泛地理解的事实,我不确定我是否一定同意这种方法- 但它可以是一个选项。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-13
    相关资源
    最近更新 更多