You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为ReactiveUI中InvokeCommand的命令参数编写单元测试

ReactiveUI响应式管道单元测试问题及解决方案

问题背景

需要编写单元测试验证ReactiveUI响应式管道逻辑,暂未找到正确实现方案。以下为ReactiveUI官方提供的响应式管道示例代码:

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 => {
            var vm = new ExampleViewModel();
            vm.Activator.Activate();

            var wasExecuted = false;
            // 思路:重写ExecuteSearch命令,获取传入参数并做校验
            vm.ExecuteSearch = ReactiveCommand.Create<string>(query => {
                // 这行代码始终不会执行
                Assert.That(query, Is.EqualTo("TestQuery"));
                wasExecuted = true;
            });
            vm.SearchQuery = "TestQuery";
            scheduler.AdvanceByMs(3000);

            Assert.That(wasExecuted, Is.True); // 此处断言失败
        });
    }

    // 示例ViewModel的实现
    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);

                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);
        }
    }
}

问题原因及修复方案

问题出在ExecuteSearch的属性定义上:它的setter方法没有触发INotifyPropertyChanged通知。InvokeCommand的内部实现依赖target.WhenAnyValue(commandProperty)来监听命令属性的变化,修改ExecuteSearch的属性定义,给setter加上属性变更通知即可让测试正常运行:

private ReactiveCommandBase<string, System.Reactive.Unit> _executeSearch;
public ReactiveCommandBase<string, System.Reactive.Unit> ExecuteSearch
{
    get => _executeSearch;
    set => this.RaiseAndSetIfChanged(ref _executeSearch, value);
}

内容的提问来源于stack exchange,提问作者ChristianMurschall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 13:15:01