为这个被动ui场景构建测试(或属性)

时间:2014-02-05 21:30:45

标签: testing reactiveui

我不确定构建此测试的正确方法。我在这里有一个视图模型:

public class ViewModel
{
  public ReactiveCommand PerformSearchCommand { get; private set; }
  private readonly ObservableAsPropertyHelper<bool> _IsBusy;
  public bool IsBusy
  {
      get { return _IsBusy.Value; }
  }

  public ViewModel(IAdventureWorksRepository _awRepository)
  {
    PerformSearchCommand = new ReactiveCommand();

    PerformSearchCommand.RegisterAsyncFunction((x) =>
    {
      return _awRepository.vIndividualCustomers.Take(1000).ToList();
    }).Subscribe(rval =>
    {
      CustomerList = rval;
      SelectedCustomer = CustomerList.FirstOrDefault();
    });

   PerformSearchCommand.IsExecuting.ToProperty(this, x => x.IsBusy, out _IsBusy);
   PerformSearchCommand.Execute(null); // begin executing immediately
  }

}

依赖项是AdventureWorks的数据访问对象

public interface IAdventureWorksRepository
{
  IQueryable<vIndividualCustomer> vIndividualCustomers { get; }
}

最后,我的测试看起来像这样:

    [TestMethod]
    public void TestTiming()
    {
        new TestScheduler().With(sched =>
        {
            var repoMock = new Mock<IAdventureWorksRepository>();

            repoMock.Setup(x => x.vIndividualCustomers).Returns(() =>
            {
                return new vIndividualCustomer[] {
                new vIndividualCustomer { FirstName = "John", LastName = "Doe" }
            };
            });

            var vm = new ViewModel(repoMock.Object);

            Assert.AreEqual(true, vm.IsBusy); //fails?
            Assert.AreEqual(1, vm.CustomerList.Count); //also fails, so it's not like the whole thing ran already

            sched.AdvanceTo(2);
            Assert.AreEqual(1, vm.CustomerList.Count); // success
            // now the customer list is set at tick 2 (not at 1?)  
            // IsBusy was NEVER true.

        });




    }

所以viewmodel应该在加载时立即开始搜索

我的直接问题是IsBusy属性似乎没有在测试调度程序中设置,即使它在我正常运行代码时似乎工作正常。我是否在视图模型中正确使用ToProperty方法?

更一般地说,当我的测试对象具有这样的依赖性时,进行完整的'时间旅行'测试的正确方法是什么?问题是,与我看到的大多数测试示例不同,被调用的接口不是IObservable。它只是一个同步查询,在我的视图模型中异步使用。当然在视图模型测试中,我可以模拟查询来做任何我需要的东西。如果我想让查询持续200个刻度,我该怎么设置呢?

1 个答案:

答案 0 :(得分:12)

所以,你的代码中有一些东西阻止你让事情正常工作:

不要在ViewModel构造函数

中调用命令

首先,在构造函数中调用Execute意味着您永远不会看到状态更改。最好的模式是编写该命令,但立即在VM中执行它,然后在视图中:

this.WhenAnyValue(x => x.ViewModel)
    .InvokeCommand(this, x => x.ViewModel.PerformSearchCommand);

异步操作后移动时钟

好的,既然我们可以正确测试之前和之后的状态,我们必须意识到每次我们做一些通常都是异步的事情之后,如果我们使用TestScheduler,我们必须推进调度程序。这意味着,当我们调用命令时,我们应该立即推进时钟:

Assert.IsTrue(vm.PerformSearchCommand.CanExecute(null));

vm.PerformSearchCommand.Execute(null);
sched.AdvanceByMs(10);

无法测试没有IObservable的时间旅行

然而,诀窍是,你的模拟立即执行代码,没有延迟,所以你永远不会看到它忙。它只返回一个罐装值。不幸的是,如果要查看IsBusy切换,注入存储库会使测试变得困难。

所以,让我们对构造函数进行一些修改:

public ViewModel(IAdventureWorksRepository _awRepository, Func<IObservable<List<Customer>>> searchCommand = null)
{
    PerformSearchCommand = new ReactiveCommand();

    searchCommand = searchCommand ?? () => Observable.Start(() => {
        return _awRepository.vIndividualCustomers.Take(1000).ToList();
    }, RxApp.TaskPoolScheduler);

    PerformSearchCommand.RegisterAsync(searchCommand)
        .Subscribe(rval => {
            CustomerList = rval;
            SelectedCustomer = CustomerList.FirstOrDefault();
        });

    PerformSearchCommand.IsExecuting
        .ToProperty(this, x => x.IsBusy, out _IsBusy);
}

立即设置测试

现在,我们可以设置测试,用一些有延迟的东西替换PerformSearchCommand的动作:

new TestScheduler().With(sched =>
{
    var repoMock = new Mock<IAdventureWorksRepository>();

    var vm = new ViewModel(repoMock.Object, () => 
        Observable.Return(new[] { new vIndividualCustomer(), })
            .Delay(TimeSpan.FromSeconds(1.0), sched));

    Assert.AreEqual(false, vm.IsBusy);
    Assert.AreEqual(0, vm.CustomerList.Count);

    vm.PerformSearchCommand.Execute(null);
    sched.AdvanceByMs(10);

    // We should be busy, we haven't finished yet - no customers
    Assert.AreEqual(true, vm.IsBusy);
    Assert.AreEqual(0, vm.CustomerList.Count);

    // Skip ahead to after we've returned the customer
    sched.AdvanceByMs(1000);

    Assert.AreEqual(false, vm.IsBusy);
    Assert.AreEqual(1, vm.CustomerList.Count);
});