mocking NewThreadScheduler.Default

本文关键字:Default NewThreadScheduler mocking | 更新日期: 2023-09-27 18:10:50

我有代码

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

据我所知,应用. subscribeon (NewThreadScheduler.Default)使IObserver在新线程中运行。所有工作都很好,但我在单元测试方面有问题。

我做了必要的更改,但是在另一个线程中运行的订阅没有等待。如何取消. subscribeon (NewThreadScheduler.Default)用于单元测试。没有这个约定,代码也能正常工作。

我试过响应式UI testScheduler.With((scheduler)=>{…});但是没有成功。我该如何解决这个问题?

mocking NewThreadScheduler.Default

您将希望在单元测试中使用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();

Scheduler.With(block => ....只有在你的代码总是使用RxApp.TaskPoolSchedulerRxApp.DeferredScheduler(对于UI线程)时才有效。如果您将NewThreadScheduler.Default更改为RxApp.TaskPoolScheduler,它应该像您期望的那样工作

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

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

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

默认情况下,SchedulerSwitch lambdas映射到各自的运行时调度器。然而,在设置测试上下文时,您可以用您所选择的一个(例如,TestSchedulerImmediateScheduler)替换运行时调度器:

testScheduler = new TestScheduler();
SchedulerSwitch.GetNewThreadScheduler = () => testScheduler;