Как выполнить модульное тестирование исключений из ReactiveCommand (ReactiveUI)? - PullRequest
1 голос
/ 26 февраля 2020

Я использую xUnit и ReactiveUI 11.2 (для WPF,. NET Framework 4.8, но я думаю, что мой вопрос более обобщенный c).

По сути, я хочу проверить свой ReactiveCommand в моделях представления.

Например, было какое-то условие, которое вызвало исключение в моем коде, и моя программа вылетает.

Я хочу сделать модульный тест, чтобы воспроизвести эту ошибку (модульный тест должен потерпеть неудачу) Затем я исправлю свою ошибку, как-то исключу исключение, и тогда мой тест должен пройти, чтобы отразить исправление. (довольно стандартная процедура).

Проблема в том, что любое исключение, выдаваемое во время ReactiveCommand, кажется "проглоченным" ReactiveUI, исключение не приводит к сбою теста .

Кроме того, то же самое происходит, если я пытаюсь написать Assert() операторы в обратном вызове .Subscribe(): во время отладки я вижу, что мое утверждение не выполняется должным образом, но тест в любом случае помечается зеленым как «пройдено».

Я попробовал другой способ, немного поиграв с планировщиками, ничего не улучшилось. Я пытался использовать .ThrownExceptions, как описано, тоже не помогает.

Некоторая документация здесь: https://reactiveui.net/docs/handbook/testing/


TL; DR

Как я могу сделать исключения в моих ReactiveCommand s, чтобы мои модульные тесты не проходили? Как я должен провести модульные тесты ReactiveCommand с?


Ниже приведена полная программа, демонстрирующая проблему.

Использование с пакетами NuGet: xunit 2.4.1, xunit.runner.visualstudio 2.4.1, ReactiveUI.Testing 11.2.1

using Microsoft.Reactive.Testing;
using ReactiveUI;
using ReactiveUI.Testing;
using System;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using Xunit;

namespace Tests
{
    public class Foo
    {

        public ReactiveCommand<Unit, Unit> TestCommand { get; }

        public Foo(IScheduler? scheduler = null)
        {
            scheduler ??= RxApp.MainThreadScheduler;

            TestCommand = ReactiveCommand.Create(Explode, canExecute: null, outputScheduler: scheduler);
        }

        public void Explode()
        {
            throw new Exception("Boom");
        }
    }

    public class ReactiveCommandTests
    {

        // Should fail? (it doesn't fail)
        [Fact]
        public void Test1()
        {
            var foo = new Foo();
            foo.TestCommand.Execute().Subscribe();
        }

        // Should fail (it fails alright ! no ReactiveUI Observable here...)
        [Fact]
        public void Test2()
        {
            var foo = new Foo();
            foo.Explode();
        }

        // Should fail? (it doesn't fail)
        [Fact]
        public void Test3()
        {
            var testScheduler = new TestScheduler();
            var foo = new Foo(testScheduler);
            foo.TestCommand.Execute().Subscribe();
        }

        // Should fail? (it doesn't fail)
        [Fact]
        public void Test4()
        {
            new TestScheduler().With(scheduler =>
            {
                var foo = new Foo(scheduler);
                foo.TestCommand.Execute().Subscribe();

            });
        }

        // Should fail ? (it doesn't fail)
        [Fact]
        public void Test5()
        {
            var foo = new Foo();
            foo.TestCommand.ThrownExceptions.Subscribe(
                (ex) => {
                    Console.WriteLine("Exception detected !");
                    Assert.False(true); // This is hit, but doesn't even make the test fail....
                });

            foo.TestCommand.Execute().Subscribe();
        }
    }
}

Все тесты вызывают исключение, все тесты должны провалиться IMO, но только один , не использующий , `ReactiveCommand терпит неудачу.

Test results that shows 4 over 5 pass instead of 0 over 5

Ответы [ 2 ]

0 голосов
/ 26 февраля 2020

После некоторых попыток, а также с помощью этой команды c: http://introtorx.com/Content/v1.0.10621.0/16_TestingRx.html я наконец-то получил то, чего не хватало.

Нужно:

  • используйте TestScheduler
  • и сообщите планировщику теста на выполнение (который немедленно выполнит все в наблюдаемой области)

Так что эта адаптация из Test3 в вопросе, кажется, работает нормально:

[Fact]
public void Test3()
{
    var testScheduler = new TestScheduler();
    var foo = new Foo(testScheduler);
    foo.TestCommand.Execute().Subscribe();
    testScheduler.Start(); // YEAY
}

В общем, мы закончили оборачивать все наши тесты, которые используют ReactiveCommands в блоке With:

new TestScheduler().With(scheduler =>
{
    var testScheduler = new TestScheduler();
    var foo = new Foo(testScheduler);
    foo.TestCommand.Execute().Subscribe();
    testScheduler.Start(); // YEAY
});

Результаты теста не пройдены, трассировка стека показывает, что это вызвано исключением:

 Tests.ReactiveCommandTests.Test3
   Source: ReactiveCommandTests.cs line 50
   Duration: 45 ms

  Message: 
    System.Exception : Boom
  Stack Trace: 
    Foo.Explode() line 26
    <>c__DisplayClass0_0.<Create>b__1(IObserver`1 observer) line 108
    CreateWithDisposableObservable`1.SubscribeCore(IObserver`1 observer) line 35
    ObservableBase`1.Subscribe(IObserver`1 observer) line 58
    --- End of stack trace from previous location where exception was thrown ---
    ExceptionDispatchInfo.Throw()
    <.cctor>b__2_1(Exception ex) line 16
    AnonymousSafeObserver`1.OnError(Exception error) line 62
    ObserveOnObserverNew`1.DrainStep(ConcurrentQueue`1 q) line 553
    ObserveOnObserverNew`1.DrainShortRunning(IScheduler recursiveScheduler) line 509
    <>c__DisplayClass4_0`1.<ScheduleAbsolute>b__0(IScheduler scheduler, TState state1) line 430
    ScheduledItem`1.Invoke() line 44
    VirtualTimeSchedulerBase`2.Start() line 174
    ReactiveCommandTests.Test3() line 56

0 голосов
/ 26 февраля 2020

@ Pac0, попробуйте это. Вам нужно будет добавить пакет FluentAssertions. Я бы также рекомендовал определить пользовательское исключение.

// Arrange
var foo = new Foo();            

// Act
var result = foo.TestCommand.Execute().Subscribe();

// Assert
result.Should().BeOfType<Exception>();
...