我有代码
var i = 0;
_searchService.FindAll()
.SubscribeOn(NewThreadScheduler.Default)
.Subscribe(i => { i++ }, () => { i *= 2; });
据我所知,应用. subscribeon (NewThreadScheduler.Default)使IObserver在新线程中运行。所有工作都很好,但我在单元测试方面有问题。
我做了必要的更改,但是在另一个线程中运行的订阅没有等待。如何取消. subscribeon (NewThreadScheduler.Default)用于单元测试。没有这个约定,代码也能正常工作。
我试过响应式UI testScheduler.With((scheduler)=>{…});但是没有成功。我该如何解决这个问题?
您将希望在单元测试中使用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.TaskPoolScheduler
或RxApp.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映射到各自的运行时调度器。然而,在设置测试上下文时,您可以用您所选择的一个(例如,TestScheduler
或ImmediateScheduler
)替换运行时调度器:
testScheduler = new TestScheduler();
SchedulerSwitch.GetNewThreadScheduler = () => testScheduler;