我在测试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
不为null并且有项。
[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的公共IOobservable吗
更新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());
});
}
WhenAnyValue
不适用于ObservableCollection
最好使用ReactiveUI附带的ReactiveList
并观察CountChanged
属性。
首先,因为您使用的是.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
。