2017-04-15 60 views
2

我正在重写一个我为了利用ReactiveUI写的WPF-App,以获得关于该库的感觉。推荐测试调度程序/调节程序的方法

我真的很喜欢它!

现在我偶然发现了Throttle方法,并希望在将过滤器应用到集合时使用它。

这是我的ViewModel:

namespace ReactiveUIThrottle 
{ 
    public class MainViewModel : ReactiveObject 
    { 
     private string _filter; 

     public string Filter { get => _filter; set => this.RaiseAndSetIfChanged(ref _filter, value); } 

     private readonly ReactiveList<Person> _persons = new ReactiveList<Person>(); 

     private readonly ObservableAsPropertyHelper<IReactiveDerivedList<Person>> _filteredPersons; 

     public IReactiveDerivedList<Person> Persons => _filteredPersons.Value; 
     public MainViewModel() 
     { 
      Filter = string.Empty; 
      _persons.AddRange(new[] 
      { 
       new Person("Peter"), 
       new Person("Jane"), 
       new Person("Jon"), 
       new Person("Marc"), 
       new Person("Heinz") 
      }); 

      var filterPersonsCommand = ReactiveCommand.CreateFromTask<string, IReactiveDerivedList<Person>>(FilterPersons); 

      this.WhenAnyValue(x => x.Filter) 
       // to see the problem 
       .Throttle(TimeSpan.FromMilliseconds(2000), RxApp.MainThreadScheduler) 
       .InvokeCommand(filterPersonsCommand); 

      _filteredPersons = filterPersonsCommand.ToProperty(this, vm => vm.Persons, _persons.CreateDerivedCollection(p => p)); 


     } 
     private async Task<IReactiveDerivedList<Person>> FilterPersons(string filter) 
     { 
      await Task.Delay(500); // Lets say this takes some time 
      return _persons.CreateDerivedCollection(p => p, p => p.Name.Contains(filter)); 
     } 
    } 
} 

本身就像一个魅力的过滤,也节流,使用GUI时。

不过,我想单元测试过滤的行为,这是我第一次尝试:

[Test] 
    public void FilterPersonsByName() 
    { 
     var sut = new MainViewModel(); 

     sut.Persons.Should().HaveCount(5); 
     sut.Filter = "J"; 
     sut.Persons.Should().HaveCount(2); 
    } 

这个测试失败,因为集合仍然有5人。

当我摆脱await Task.Delay(500)FilterPersons然后测试会通过,但需要2秒(从油门)。

1)有没有一种方法可以在测试中使油门瞬间加速单元测试?

2)如何测试我的过滤器中的异步行为?

我使用ReactiveUI 7.x的

回答

2

简短回答:

  1. 是,通过确保你在测试运行时,使用CurrentThreadScheduler.Instance
  2. 而不是使用CurrentThreadScheduler,使用TestScheduler并手动推进它

较长的答案是,你需要确保你的单元测试可以控制t他的调度程序正在被测系统(SUT)使用。默认情况下,您通常会希望使用CurrentThreadScheduler.Instance“立即”完成任务,而无需手动提前调度程序。但是当你想编写确认时间的测试时,你可以使用TestScheduler

如果,你似乎是,你使用RxApp.*Scheduler,看看在With扩展方法,它可以这样使用:

我倾向于避免使用RxApp周围语境总之,出于同样的原因,我避免了所有的环境情况:它们是共享状态,并可能导致麻烦。相反,我注入一个IScheduler(或两个)到我的SUT作为依赖。

+0

谢谢您的回复。作为生产代码运行时,你会注入什么模型? 'RxApp.MainThreadScheduler'? 。 –

+0

更新: [测试] 公共无效FilterPersonsByName() { 新TestScheduler()同(SCHED => { 变种SUT =新MainViewModel(附表); SCHED。AdvanceByMs(3000); sut.Persons.Should()。HaveCount(5); sut.Filter =“J”; sched.AdvanceByMs(3000); sut.Persons.Should()。HaveCount(2); }); } 的作品。所以第二点是workign :-)如果我给它'CurrentThreadScheduler.Instace'它仍然运行2-3秒,而不是即时 –

+0

如果你愿意,你当然可以在运行时使用'RxApp'调度器。就我个人而言,我自己创建调度程序并注入它们,但这是个人偏好,而不是必要性。 –