2013-04-24 4 views
0

У меня есть коднасмешливый NewThreadScheduler.Default

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

Насколько я знаю, применяя .SubscribeOn (NewThreadScheduler.Default) сделать IObserver работать в новом потоке. Все работает хорошо, но у меня проблема с модульными тестами.

Я делаю необходимые изменения, но эта подписка, запущенная в другом треде, не ждет. Как отменить .SubscribeOn (NewThreadScheduler.Default) для модульных тестов. Код работает без этого назначения.

Я пробовал реактивный UI testScheduler.With ((scheduler) => {... напишите этот код здесь ...}); но нет успеха. Как я могу решить эту проблему?

+0

Если вы используете RxUI могли бы вы попробовать использовать RxApp.TaskpoolScheduler вместо NewThreadScheduler.Default и посмотреть, помогает ли это? – Dtex

ответ

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, но вы можете просто изменить его (удаление или добавление), как вы считаете нужным.

Я попытался подробно объяснить, как проверить Rx с TestScheduler на моем сайте здесь 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(); 
2

Scheduler.With(block => .... работает только, если ваш код всегда использует RxApp.TaskPoolScheduler или RxApp.DeferredScheduler (для UI Thread). Если вы измените свой NewThreadScheduler.Default на RxApp.TaskPoolScheduler, он должен работать так, как вы ожидаете.

0

Я решил эту же проблему, создав пакет NuGet, который реализует подход, подобный ISchedulerProvider, предложенный @Lee Campbell. Он состоит из переключателя ", который я использую в качестве замены для класса Scheduler. Для удобства я опубликовал его как пакет NuGet по адресу https://nuget.org/packages/RxSchedulers.Switch/.

Чтобы использовать его адаптировать свой код следующим образом:

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

По умолчанию карты SchedulerSwitch Лямбд соответствующих во время выполнения планировщиков. Тем не менее, при настройке контекста теста, вы можете заменить выполнения планировщика по одному вашему выбору (например, TestScheduler или ImmediateScheduler):

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

Как раз то, что ваш SchedulerSwitch кажется статическим классом. Похоже, что это закончится слезами.Вы не сможете запускать параллельные параллельные тесты, и вы никогда не узнаете это состояние статического объекта, который вы используете. Вот почему люди используют интерфейсы и IoC/DI. –

+0

Нет, вам просто нужно быть более умным в определении свойства: https://github.com/reactiveui/ReactiveUI/blob/rxui5-master/ReactiveUI/RxApp.cs#L128 –

+0

Хорошая точка @ LeeCampbell, и смешно потому что решение, которое сразу пришло в голову, было тем, что предложил Пол Беттс. –

Смежные вопросы