1

我有一个代码

var i = 0;
_searchService.FindAll()
    .SubscribeOn(NewThreadScheduler.Default)
    .Subscribe(i => { i++ }, () => { i *= 2; });

据我所知,应用.SubscribeOn(NewThreadScheduler.Default)使 IObserver 在新线程中运行。一切正常,但我对单元测试有疑问。

我进行了必要的更改,但在另一个线程中运行的此订阅不会等待。如何取消.SubscribeOn(NewThreadScheduler.Default)进行单元测试。没有这个任命,代码运行良好。

我已经尝试过响应式 UI testScheduler.With((scheduler)=>{... 在此处编写此代码...}); 但没有成功。我怎么解决这个问题?

4

3 回答 3

4

您将希望使用 TestScheduler 代替 NewThreadScheduler 进行单元测试。我假设您使用 IoC 作为设计模式来启用您的单元测试,那么您需要做的就是创建一个 ISchedulerProvider/ISchedulerService/... 接口来公开您的需求。这是我使用的东西

public interface ISchedulerProvider
{
    /// <summary>
    /// Provides access to scheduling onto the UI Dispatcher. 
    /// </summary>
    IScheduler Dispatcher { get; }

    /// <summary>
    /// Provides concurrent scheduling. Will use the thread pool or the task pool if available.
    /// </summary>
    IScheduler Concurrent { get; }

    /// <summary>
    /// Provides concurrent scheduling for starting long running tasks. Will use a new thread or a long running task if available. Can be used to run loops more efficiently than using recursive scheduling.
    /// </summary>
    ISchedulerLongRunning LongRunning { get; }

    /// <summary>
    /// Provides support for scheduling periodic tasks. Can be used to run timers more efficiently than using recursive scheduling.
    /// </summary>
    ISchedulerPeriodic Periodic { get; }
}

public sealed class SchedulerProvider : ISchedulerProvider
{
    private readonly IScheduler _dispatcherScheduler;

    public SchedulerProvider()
    {
        var currentDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
        _dispatcherScheduler = new DispatcherScheduler(currentDispatcher);
    }

    public IScheduler Dispatcher
    {
        get { return _dispatcherScheduler; }
    }


    public IScheduler Concurrent
    {
        get { return TaskPoolScheduler.Default; }
    }

    public ISchedulerLongRunning LongRunning
    {
        get { return TaskPoolScheduler.Default.AsLongRunning(); }
    }

    public ISchedulerPeriodic Periodic
    {
        get { return TaskPoolScheduler.Default.AsPeriodic(); }
    }
}

然后在您的测试中,您将使用返回 TestScheduler 实现的实现。

public sealed class TestSchedulerProvider : ISchedulerProvider
{
    private readonly TestScheduler _dispatcher = new TestScheduler();
    private readonly TestScheduler _concurrent = new TestScheduler();
    private readonly TestScheduler _longRunning = new TestScheduler();
    private readonly TestScheduler _periodic = new TestScheduler();


    IScheduler ISchedulerProvider.Dispatcher
    {
        get { return _dispatcher; }
    }
    public TestScheduler Dispatcher
    {
        get { return _dispatcher; }
    }

    IScheduler ISchedulerProvider.Concurrent
    {
        get { return _concurrent; }
    }
    public TestScheduler Concurrent
    {
        get { return _concurrent; }
    }

    ISchedulerLongRunning ISchedulerProvider.LongRunning
    {
        get { return _longRunning.AsLongRunning(); }
    }
    public TestScheduler LongRunning
    {
        get { return _longRunning; }
    }

    ISchedulerPeriodic ISchedulerProvider.Periodic
    {
        get { return _periodic.AsPeriodic(); }
    }
    public TestScheduler Periodic
    {
        get { return _periodic; }
    }
}

如您所见,这是针对 WPF 项目的,但您可以根据需要对其进行更改(删除或添加)。

我试图详细解释如何在我的网站上使用 TestScheduler 测试 Rx http://introtorx.com/Content/v1.0.10621.0/16_TestingRx.html

我不太了解您的示例代码在做什么,但我认为您可能想将其更新为

var testScheduler = new TestScheduler();
var i = 0;
var subscription = _searchService
              .FindAll()
              .SubscribeOn(testScheduler)
              .Subscribe(
                 i =>i++, 
                 () => i*=2);
Assert.AreEqual(0, i);
testScheduler.AdvanceBy(1);
Assert.AreEqual(1, i);
subscription.Dispose();
于 2013-04-24T14:40:53.877 回答
2

Scheduler.With(block => ....仅当您的代码始终使用RxApp.TaskPoolSchedulerRxApp.DeferredScheduler(对于 UI 线程)时才有效。如果您更改NewThreadScheduler.DefaultRxApp.TaskPoolScheduler,它应该可以按预期工作。

于 2013-04-24T17:55:03.003 回答
0

我通过创建一个 NuGet 包解决了同样的问题,该包实现了一种类似于@Lee Campbell 建议的 ISchedulerProvider 的方法。它由一个“ switch ”类组成,我将其用作Scheduler该类的替代品。为方便起见,我已在https://nuget.org/packages/RxSchedulers.Switch/将其作为 NuGet 包发布。

要使用它,请按如下方式调整您的代码:

var i = 0;
_searchService.FindAll()
              .SubscribeOn(SchedulerSwitch.GetNewThreadScheduler())
              .Subscribe(i =>
                             {
                                i++
                             }, () =>
                             {
                                i*=2; });

默认情况下,SchedulerSwitchlambda 映射到相应的运行时调度程序。但是,在设置测试上下文时,您可以将运行时调度程序替换为您选择的一种(例如 aTestSchedulerImmediateScheduler):

testScheduler = new TestScheduler();
SchedulerSwitch.GetNewThreadScheduler = () => testScheduler;
于 2013-04-24T21:29:23.717 回答