RX + TPL +测试

时间:2016-11-23 09:56:57

标签: c# testing asynchronous task-parallel-library system.reactive

我目前正在努力解决在`RX语句中测试TPL函数的问题。问题还在于我无法访问observable。

这是我的测试:

[Test]
public void TestRaceConditions()
{
    // Arrange
    TestScheduler testScheduler = new TestScheduler();
    List<string> source = new List<string> {"Hello"};
    List<string> results = new List<string>();

    source.ToObservable().SelectMany(v => Task.Run(() => 
    {
        Thread.Sleep(10000); // Simulate long running operation
        return v;
    }).ToObservable(testScheduler))
    .ObserveOn(testScheduler)
    .Subscribe(s => results.Add(s));

    // Act
    testScheduler.Start();

    Assert.That(results.Count, Is.EqualTo(1));
    Assert.That(results[0], Is.EqualTo("Hello"));
}

我已经搜索过,发现了许多文章和建议,如:

  • 请勿混用TPLRX
  • TPL代码创建一个包装器,您可以在其中模拟并返回已完成的任务。 ==&GT;但是集成测试呢?

我还尝试创建自定义TaskScheduler,但我必须用Task.Run替换每个Task.Factory.StartNew

所以问题是:

  • 如何在处理完所有元素的时候用Schedulers来了解测试中的任务或观察中的observable?

更新:真实示例

public class ResultController : IDisposable
{
    private readonly IServiceClient _serviceClient;
    private readonly IDisposable _subscription;

    public ResultController(IEventProvider eventProvider, 
                            IServiceClient serviceClient, 
                            ISchedulerProvider schedulerProvider)
    {
        _serviceClient = serviceClient;
        _subscription = eventProvider.Events.SelectMany(e => LoadDataAsync(e.Id))
                                            .SubscribeOn(schedulerProvider.TaskPool)
                                            .ObserveOn(schedulerProvider.Dispatcher)
                                            .Subscribe(OnNewDataLoaded);
    }

    private void OnNewDataLoaded(Data data)
    {

    }

    public async Task<Data> LoadDataAsync(int id)
    {
        ////return Task.Run(() =>
        ////{
        ////    Thread.Sleep(10000);
        ////    return new Data();
        ////});

        return await _serviceClient.LoadDataByIdAsync(id);
    }

    public void Dispose()
    {
        _subscription.Dispose();
    }
}

此解决方案在生产中无问题地工作,但在测试中我没有控制任务。

  • 模拟ServiceClient并返回已完成的Task可以正常工作。
  • 但是在使用SpecFlow的IntegrationTests中,我不知道工作何时完成而没有引入黑客属性进行同步。

1 个答案:

答案 0 :(得分:0)

我认为这里发生的事情是你在完成工作之前断言工作的结果:

// starts a work in a different thread
// and continues the test right after firing work up
testScheduler.Start();

// results here are empty for a 10 seconds
Assert.That(results.Count, Is.EqualTo(1));
Assert.That(results[0], Is.EqualTo("Hello"));

你可以在这里做的是订阅Task.Run所安排的任务的继续,你也可以引入一系列任务并将任务添加到它并等待它们,并且只有在那之后你的支票。

由于您的模拟工作,引入自定义调度程序可能没有用 - 您仍然需要等待10秒才能唤醒线程。也许另一种选择是使用Task.Delay方法,测试调度程序可以忽略它。