推荐的测试方式 Scheduler/Throttle
Recommended way to test Scheduler/Throttle
我正在重写我为使用 ReactiveUI 而编写的一个小 WPF 应用程序,以了解该库。
到目前为止我真的很喜欢它!
现在我偶然发现了 Throttle
方法,想在对集合应用过滤器时使用它。
这是我的视图模型:
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人
当我摆脱 FilterPersons
中的 await Task.Delay(500)
时,测试将通过,但需要 2 秒(从油门开始)。
1) 有没有办法让测试中的油门瞬间加速单元测试?
2) 我将如何测试过滤器中的异步行为?
我正在使用 ReactiveUI 7.x
简答:
- 是的,通过确保您在 运行 测试中使用
CurrentThreadScheduler.Instance
- 不使用
CurrentThreadScheduler
,而是使用 TestScheduler
并手动推进它
较长的答案是您需要确保您的单元测试可以控制被测系统 (SUT) 使用的调度程序。默认情况下,您通常希望使用 CurrentThreadScheduler.Instance
让事情发生 "instantly" 而无需手动推进调度程序。但是当你想编写验证时序的测试时,你可以使用 TestScheduler
代替。
如果您使用的是 RxApp.*Scheduler
,请查看 With
扩展方法,它可以像这样使用:
(new TestScheduler()).With(sched => {
// write test logic here, and RxApp.*Scheduler will resolve to the chosen TestScheduler
});
我倾向于完全避免使用 RxApp
环境上下文,原因与我避免使用所有环境上下文的原因相同:它们是共享状态,因此可能会导致麻烦。相反,我将 IScheduler
(或两个)作为依赖项注入到我的 SUT 中。
我正在重写我为使用 ReactiveUI 而编写的一个小 WPF 应用程序,以了解该库。
到目前为止我真的很喜欢它!
现在我偶然发现了 Throttle
方法,想在对集合应用过滤器时使用它。
这是我的视图模型:
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人
当我摆脱 FilterPersons
中的 await Task.Delay(500)
时,测试将通过,但需要 2 秒(从油门开始)。
1) 有没有办法让测试中的油门瞬间加速单元测试?
2) 我将如何测试过滤器中的异步行为?
我正在使用 ReactiveUI 7.x
简答:
- 是的,通过确保您在 运行 测试中使用
CurrentThreadScheduler.Instance
- 不使用
CurrentThreadScheduler
,而是使用TestScheduler
并手动推进它
较长的答案是您需要确保您的单元测试可以控制被测系统 (SUT) 使用的调度程序。默认情况下,您通常希望使用 CurrentThreadScheduler.Instance
让事情发生 "instantly" 而无需手动推进调度程序。但是当你想编写验证时序的测试时,你可以使用 TestScheduler
代替。
如果您使用的是 RxApp.*Scheduler
,请查看 With
扩展方法,它可以像这样使用:
(new TestScheduler()).With(sched => {
// write test logic here, and RxApp.*Scheduler will resolve to the chosen TestScheduler
});
我倾向于完全避免使用 RxApp
环境上下文,原因与我避免使用所有环境上下文的原因相同:它们是共享状态,因此可能会导致麻烦。相反,我将 IScheduler
(或两个)作为依赖项注入到我的 SUT 中。