Search code examples
c#reactiveui

Write unit test for command parameter in InvokeCommand


I would like to write an unit test to check my pipeline and don't get the right idea to to so. As an example let's take the pipeline directly from their website:

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

I my Idea was to overwrite in my unit test the ExecuteSearch command and check the provided parameter. But this does not work, the original command gets executed.

Here is my example test fixture, how I would write the test, but perhaps I'm on the wrong track?

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

Edit:

Thanks to Krzysztof's answer below I found out that the setter of ExecuteSearch needs to raise INPT because the implementation of InvokeCommand looks for target.WhenAnyValue(commandProperty). With this change, it works like a charm :)


Solution

  • I changed the pipeline to:

    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 also needs to raise INotifyPropertyChanged.PropertyChanged event.

    It will keep the reference up to date.