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

 金燁欣_973 发布于 2023-01-29 10:16

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

public class ViewModel
{
  public ReactiveCommand PerformSearchCommand { get; private set; }
  private readonly ObservableAsPropertyHelper _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 vIndividualCustomers { get; }
}

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

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

            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 个回答
  • 所以,你的代码中有一些东西阻止你让事情正常工作:

    不要在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,无法测试Time Travel

    然而,诀窍是,你的模拟立即执行代码,没有延迟,所以你永远不会看到它忙.它只返回一个罐装值.不幸的是,如果要查看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);
    });
    

    2023-01-29 10:17 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有