【问题标题】:Recommended way to test Scheduler/Throttle测试调度程序/油门的推荐方法
【发布时间】:2017-04-15 20:19:21
【问题描述】:

我正在重写我编写的一个小 WPF 应用程序以利用 ReactiveUI,以了解该库。

到目前为止我真的很喜欢它!

现在我偶然发现了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 个人。

当我摆脱 FilterPersons 中的 await Task.Delay(500) 时,测试将通过,但需要 2 秒(从油门开始)。

1) 有没有办法让测试中的油门即时加速单元测试?

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

我正在使用 ReactiveUI 7.x

【问题讨论】:

    标签: reactiveui


    【解决方案1】:

    简短回答:

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

    更长的答案是,您需要确保您的单元测试可以控制您的被测系统 (SUT) 使用的调度程序。默认情况下,您通常希望使用CurrentThreadScheduler.Instance 让事情“立即”发生,而无需手动推进调度程序。但是,当您想编写验证时序的测试时,您可以改用TestScheduler

    如果您似乎使用的是RxApp.*Scheduler,请查看With 扩展方法,它可以像这样使用:

    (new TestScheduler()).With(sched => {
        // write test logic here, and RxApp.*Scheduler will resolve to the chosen TestScheduler
    });
    

    我倾向于完全避免使用 RxApp 环境上下文,原因与我避免所有环境上下文的原因相同:它们是共享状态,因此可能会导致麻烦。相反,我将一个 IScheduler(或两个)作为依赖项注入到我的 SUT 中。

    【讨论】:

    • 感谢您的回复。当作为生产代码运行时,你会在模型中注入什么? RxApp.MainThreadScheduler?
    • 更新:[Test] public void FilterPersonsByName() { new TestScheduler().With(sched => { var sut = new MainViewModel(sched); sched.AdvanceByMs(3000); sut.Persons。应该().HaveCount(5); sut.Filter = "J"; sched.AdvanceByMs(3000); sut.Persons.Should().HaveCount(2); }); } 工作。所以第二点是锻炼 :-) 如果我给它CurrentThreadScheduler.Instace 它仍然运行 2-3 秒而不是即时
    • 如果您愿意,当然可以在运行时使用RxApp 调度程序。就个人而言,我自己创建调度程序并注入它们,但这是个人喜好,而不是必要性。
    猜你喜欢
    • 2013-04-04
    • 2011-04-17
    • 2011-11-08
    • 1970-01-01
    • 1970-01-01
    • 2012-02-04
    • 1970-01-01
    • 1970-01-01
    • 2018-05-19
    相关资源
    最近更新 更多