2017-02-17 6 views
3

Я принимаю код от ReactiveUi website documentation и пытаюсь выполнить его тестирование, но он не работает.
Речь идет о вызове команды для отмены другого.ReactiveUI - Отменить команду от другого - Не удалось выполнить тестирование кода из документации

Ниже приведен тест.

public class SomeViewModel : ReactiveObject 
{ 
    public SomeViewModel(IScheduler scheduler) 
    { 
     this.CancelableCommand = ReactiveCommand 
      .CreateFromObservable(
       () => Observable 
        .StartAsync(DoSomethingAsync) 
        .TakeUntil(CancelCommand), outputScheduler: scheduler); 
     this.CancelCommand = ReactiveCommand.Create(
      () => 
      { 
       Debug.WriteLine("Cancelling"); 
      }, 
      this.CancelableCommand.IsExecuting, scheduler); 
    } 

    public ReactiveCommand<Unit, Unit> CancelableCommand 
    { 
     get; 
     private set; 
    } 

    public ReactiveCommand<Unit, Unit> CancelCommand 
    { 
     get; 
     private set; 
    } 

    public bool IsCancelled { get; private set; } 

    private async Task DoSomethingAsync(CancellationToken ct) 
    { 
     try 
     { 
      await Task.Delay(TimeSpan.FromSeconds(3), ct); 
     } 
     catch (TaskCanceledException) 
     { 
      IsCancelled = true; 
     } 
    } 
} 

А вот тест блок:

[TestFixture] 
[Category("ViewModels")] 
public class SomeViewModelFixture 
{ 
    public async Task Executing_cancel_should_cancel_cancelableTask() 
    { 
     var sut = new SomeViewModel(Scheduler.Immediate); 
     var t = sut.CancelableCommand.Execute(); 
     await sut.CancelCommand.Execute(); 

     await t; 

     Assert.IsTrue(sut.IsCancelled); 
    } 
} 

CancelCommand является excuted (контрольная точка на бревне консоли ударил) однако Task.Delay никогда не отменяется. Кажется TakeUntil не просит отмены.

UPDATE
Я редактировал код выше, так что мой ViewModel т е р принимает IScheduler и создавать команды с ним в соответствии с that issue about unit testing но тест по-прежнему не удается.
Я пробовал как nUnit, так и xUnit.
Я также попытался сделать RxApp.MainThreadScheduler = Scheduler.Immediate в моей тестовой настройке согласно that article, но все еще не удается.

UPDATE2
Из решения, что я отметил в качестве ответа и комментариев, проще всего не использовать IScheduler в CTOR, а затем написать тест, как и что она проходит.

[Test] 
    public async Task Executing_cancel_should_cancel_cancelableTask() 
    { 
     var sut = new SomeViewModel(); 
     sut.CancelableCommand.Execute().Subscribe(); 
     await sut.CancelCommand.Execute(); 

     Assert.IsTrue(sut.IsCancelled); 
    } 
+0

Возможно, вам нужно взглянуть на использование тестового бегуна, который не отложен. Быстрый поиск нашел эту статью, которая может помочь. http://putridparrot.com/blog/unit-testing-a-reactiveui-viewmodel/ – kenny

+0

Tks но: i) Я не совсем понимаю ii) RxApp не обладает свойством aDeferredScheduler ii) в статье автор говорит, что реактивное устройство обнаруживало тестировщики и выполняли переключатель планировщика. –

+0

DeferredScheduler был переименован в MainThreadScheduler. Я сделал это планировщиком Immediate, но тест по-прежнему терпит неудачу. Я использую NUnit. Попробуем с xUnit. –

ответ

3

Это работает для меня:

public class SomeViewModelTest 
{ 
    SomeViewModel m_actual; 

    [SetUp] 
    public void Setup() 
    { 
     m_actual = new SomeViewModel(CurrentThreadScheduler.Instance); 
     m_actual.Activator.Activate(); 
    } 

    [Test] 
    public void Executing_cancel_should_cancel_cancelableTask() 
    { 
     m_actual.CancelableCommand.Execute().Subscribe(); 
     m_actual.CancelCommand.Execute().Subscribe(); 

     Assert.IsTrue(m_actual.IsCancelled); 
    } 
} 

Я изменил планировщик, чтобы использовать то же самое от самого теста, и мой ViewModel действительно осуществляет ISupportsActivation, что я осмелюсь сказать, не будет делать какие-либо различия здесь. Другое, тогда я удалил асинхронный/ждущий тест, вам не нужно, что с Rx, и просто подписался на эту команду.

+1

Действительно, подписка делает работу (а не настройку или активатор). Однако я не понимаю, почему. Я посмотрю глубже в документ. Tks в любом случае :-) –

+0

Планировщик тоже бесполезен. Решение исходит только от подписки. –

+1

Итак, мысль об этом ... Когда вы вызываете Execute, она вызывает функцию, которую вы передали в ReactiveCommand. Таким образом, в вашем случае эта функция является Observable.StartAsync, которая извлекает задачу, но в этот момент наблюдаемая сама по-прежнему не подписана, хотя задача была восстановлена. Таким образом, в вашем исходном коде, где вы ожидаете Отмена, «CancelableCommand» по-прежнему не был начат технически. Итак, CancelCommand запускается, а затем завершается, а затем после этого вы «ждете t», но теперь CancelCommand уже запущен. Надеюсь, это имеет смысл. –

1

Проблема в том, что вы ожидаете выполнения команды после, вы ее отменяете. Поскольку аннулирование происходит до выполнения команды, это не влияет на выполнение команды. Вы можете получить его следующим образом:

public async Task Executing_cancel_should_cancel_cancelableTask() 
{ 
    var sut = new SomeViewModel(Scheduler.Immediate); 
    sut.CancelableCommand.Execute().Subscribe(); 
    await sut.CancelCommand.Execute(); 

    Assert.True(sut.IsCancelled); 
} 

Здесь я начинаю выполнение команды немедленно (путем подписки). Затем последующее отмена влияет на это выполнение.

Как правило, это всегда путаное смешивание Rx и TPL. Это работает, но есть ловушки и гадости, подобные этому, скрывающимся вокруг каждого угла. В качестве более долгосрочного решения я настоятельно рекомендую перейти на «чистый» Rx. Вы не оглядитесь назад - это потрясающе.

+0

Ожидание задания после отмены его работы с задачами. Я буду принимать ваши советы и избегать смешивания задач и наблюдаемых. –