【问题标题】:Testing ReactiveCommand and ReactiveObject ViewModels测试 ReactiveCommand 和 ReactiveObject ViewModel
【发布时间】:2015-12-02 23:21:53
【问题描述】:

我在测试 ReactiveCommand 及其执行能力时遇到了问题。

给定以下视图模型:

    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 是否可以执行。使用以下更新的虚拟机和测试我得到了同样的错误。

    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());
         });
    }

【问题讨论】:

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


    【解决方案1】:

    WhenAnyValue 不适用于ObservableCollection

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

    【讨论】:

    • 我在印象中,我相信我正在使用它来“观察”任何引发 INPC 的事情。
    • ObservableCollection 引发 INotifyCollectionChanged,而不是 INotifyPropertyChanged
    • 测试建议的更改后,我仍然看到相同的错误,使用异步会导致测试运行程序不返回。
    【解决方案2】:

    首先,因为您使用的是.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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多