在 InvokeCommand 中为命令参数编写单元测试

Write unit test for command parameter in InvokeCommand

我想编写一个单元测试来检查我的管道,但没有正确的想法。 作为一个例子,让我们直接从他们的网站上获取管道:

this.WhenAnyValue(x => x.SearchQuery)
    .Throttle(TimeSpan.FromSeconds(0.8), RxApp.TaskpoolScheduler)
    .Select(query => query?.Trim())
    .DistinctUntilChanged()
    .Where(query => !string.IsNullOrWhiteSpace(query))
    .ObserveOn(RxApp.MainThreadScheduler)
    .InvokeCommand(ExecuteSearch);

我的想法是在我的单元测试中覆盖 ExecuteSearch 命令并检查提供的参数。但这不起作用,原来的命令被执行了。

这是我的示例测试夹具,我将如何编写测试,但也许我走错了路?

[TestFixture]
public class TestInvokeCommand
{

    [Test]
    public void TestPipeline()
    {
        new TestScheduler().With(scheduler => {
            // implementation of ExampleViewModel provided below
            var vm = new ExampleViewModel();
            vm.Activator.Activate();

            var wasExecuted = false;
            // Idea: override 'ExecuteSearch Command' so we have access to
            // the command parameter and write our validation code.
            vm.ExecuteSearch = ReactiveCommand.Create<string>(query => {
                // this is never executed
                Assert.That(query, Is.EqualTo("TestQuery"));
                wasExecuted = true;
            });
            vm.SearchQuery = "TestQuery";
            scheduler.AdvanceByMs(3000);

            Assert.That(wasExecuted, Is.True); // fails here
        });
    }


    // internal class to show how the viewmodel would look like
    class ExampleViewModel : ReactiveObject, IActivatableViewModel
    {
        public ViewModelActivator Activator { get; } = new();

        public ExampleViewModel()
        {
            this.WhenActivated(disposables => {

                ExecuteSearch = ReactiveCommand.CreateFromTask<string>(async (queryString, token) 
                    => await Task.Delay(100, token))
                                 .DisposeWith(disposables);

                // directly taken from https://www.reactiveui.net/
                this.WhenAnyValue(x => x.SearchQuery)
                    .Throttle(TimeSpan.FromSeconds(0.8), RxApp.TaskpoolScheduler)
                    .Select(query => query?.Trim())
                    .DistinctUntilChanged()
                    .Where(query => !string.IsNullOrWhiteSpace(query))
                    .ObserveOn(RxApp.MainThreadScheduler)
                    .InvokeCommand(ExecuteSearch)
                    .DisposeWith(disposables);
            });
        }

        public ReactiveCommandBase<string, System.Reactive.Unit> ExecuteSearch { get; set; }

        private string _searchQuery;
        public string SearchQuery
        {
            get => _searchQuery;
            set => this.RaiseAndSetIfChanged(ref _searchQuery, value);
        }
    }
}

编辑:

感谢 Krzysztof 在下面的回答,我发现 ExecuteSearch 的 setter 需要提高 INPT,因为 InvokeCommand 的实现寻找 target.WhenAnyValue(commandProperty)。有了这个改变,它就像一个魅力:)

我将管道更改为:

this.WhenAnyValue(x => x.SearchQuery)
    .Throttle(TimeSpan.FromSeconds(0.8), RxApp.TaskpoolScheduler)
    .Select(query => query?.Trim())
    .DistinctUntilChanged()
    .Where(query => !string.IsNullOrWhiteSpace(query))
    .ObserveOn(RxApp.MainThreadScheduler)
    .InvokeCommand(this, x => x.ExecuteSearch);

ExecuteSearch 还需要引发 INotifyPropertyChanged.PropertyChanged 事件。

它将使参考保持最新。