测试ReactiveCommand和ReactiveObject ViewModels

时间:2015-02-16 02:53:35

标签: c# observablecollection system.reactive reactive-programming reactiveui

我在测试ReactiveCommands及其执行能力方面遇到了麻烦。

鉴于以下视图模型:

    public class HowToTestViewModel : ReactiveObject
    {
        public ReactiveCommand<Unit> CommandThatDoesStuff { get; set; }
        public IObservable<bool> CanExecute { get; set; }

        public ObservableCollection<string> TheCollectionMustHaveItems { get; set; }

        public HowToTestViewModel()
        {
            TheCollectionMustHaveItems = new ObservableCollection<string>();

            this.CanExecute = this.WhenAnyValue(vm => vm.TheCollectionMustHaveItems)
                .Do(debug => Console.WriteLine("Can Execute Value: " + debug.Count()))
                .Where(col => col.Any())
                .Do(debug => Console.WriteLine("Can Execute (not empty?) Value: " + debug.Count()))
                .Select(x => true);

            CommandThatDoesStuff = ReactiveCommand.CreateAsyncTask(this.CanExecute, async param => {
                //web service or something...
                await Task.Delay(1000);
            });

            CommandThatDoesStuff.Subscribe(next => Console.WriteLine("The command fired and no exceptions were thrown."));

            CommandThatDoesStuff.ThrownExceptions.Subscribe(error => Console.WriteLine("boom. handle your business here."));

        }
    }

我想测试以确保命令无法执行,除非TheCollectionMustHaveItems不为空且有项目。

    [Test]
    public void CanExecute_spec()
    {
        (new TestScheduler()).With(sched =>
        {
            var sut = new HowToTestViewModel();

            sut.CommandThatDoesStuff.Execute(null);

            sut.CommandThatDoesStuff.CanExecute(null).Should().BeFalse();

            sut.TheCollectionMustHaveItems.Should().BeEmpty();

            sut.TheCollectionMustHaveItems.Add("item added"); //CanExecute should update

            sched.AdvanceBy(1000);

            sut.CommandThatDoesStuff.CanExecute(null).Should().BeTrue();

        });
    }

我正在使用TestScheduler,但我不知道为什么。我的问题是:

  • 如何让测试通过?
  • 我应该在命令上测试CanExecute吗?
  • 我应该使用ToPropery和ObservableAsPropertyHelper而不是CanExecute的公共IObservable吗?

更新1

根据以下建议,我将ObservableCollection更改为ReactiveList,我(我想)我使用TheCollectionMustHaveItems.CountChanged来验证CommandThatDoesStuff是否可以执行。使用以下更新的VM和测试我得到相同的错误。

    public class HowToTestViewModel : ReactiveObject
    {
         ....

        public IReactiveList<string> TheCollectionMustHaveItems { get; set; }

        public HowToTestViewModel()
        {
            TheCollectionMustHaveItems = new ReactiveList<string>();

            this.CanExecute = this
                .TheCollectionMustHaveItems.CountChanged
                .Do(next => Console.WriteLine("logging the next result:" + next))
                .ObserveOn(RxApp.MainThreadScheduler)
                .Where(collection => TheCollectionMustHaveItems.Any())
                .Select(x => true);

        ...

        }
    }

更新2

将测试更新为await CommandThatDoesStuff会导致测试运行器永远不会返回。

    [Test]
        public async void CanExecute_spec()
        {
            await (new TestScheduler()).With(async sched =>
             {
             var sut = new HowToTestViewModel();

             await sut.CommandThatDoesStuff.ExecuteAsyncTask(null);

             sut.CommandThatDoesStuff.CanExecute(null).Should().BeFalse();

             sut.TheCollectionMustHaveItems.Should().BeEmpty();

             sut.TheCollectionMustHaveItems.Add("item added"); //CanExecute should update

             sut.CanExecute.Subscribe(next => next.Should().BeTrue());

             sched.AdvanceBy(1000);

             sut.CommandThatDoesStuff.CanExecuteObservable.Subscribe(next => next.Should().BeTrue());
         });
    }

2 个答案:

答案 0 :(得分:2)

WhenAnyValue不适用于ObservableCollection

最好的办法是使用ReactiveUI附带的ReactiveList并观察CountChanged属性。

答案 1 :(得分:1)

首先,因为您使用.Where后跟.Select,所以CanExecute只会返回true个值。将其更改为:

this.CanExecute = this
    .TheCollectionMustHaveItems.CountChanged
    .Log(this, "next result")   // RxUI has a nice Log extension method that can replace your previous usage of Do
    .ObserveOn(RxApp.MainThreadScheduler)
    .Select(count => count > 0);

其次,async测试返回类型应为Task,而不是void