ReactiveUI - testing subscribe when using throttle
Asked Answered
R

1

5

I have a problem when trying to unit test a view model that uses WhenAnyValue to listen for changes in a property, and populate a list based on the new value of the property. I need to use Throttle when listening to changes on the property due to interaction with 3'rd party software.

My solution works in production, but I have some trouble with my unit test. It seems to be related to not getting the test scheduler to advance correctly, so that the subscription after the throttle is actually run. I have created a simplified version of my problem which I hope is illustrative.

View model to test

public class ViewModel : ReactiveObject
{

    public ViewModel(IScheduler scheduler)
    {
        ListToBeFilled = new List<int>();

        this.WhenAnyValue(vm => vm.SelectedValue)
            .Throttle(TimeSpan.FromMilliseconds(500), scheduler)
            .Skip(1)
            .Subscribe(value =>
            {
                // Do a computation based on value and store result in
                // ListToBeFilled.
                ListToBeFilled = new List<int>() {1, 2, 3};
            });

    }

    private string _selectedValue;
    public string SelectedValue
    {
        get { return _selectedValue; }
        set { this.RaiseAndSetIfChanged(ref _selectedValue, value); }
    }

    public List<int> ListToBeFilled { get; set; } 
}

View model test

[TestFixture]
[RequiresSTA]
public class ViewModelTests
{
    [Test]
    public void TestViewModel()
    {
        // Arrange
        (new TestScheduler()).With(scheduler =>
        {
            ViewModel vm = new ViewModel(scheduler);

            // Act
            vm.SelectedValue = "test value";
            scheduler.AdvanceByMs(1000);

            // Assert
            Assert.AreEqual(3, vm.ListToBeFilled.Count);
        });
    }
}

The test fails saying Expected: 3 But was 0. When running the test without using Throttle (which I need to get things to work in production), the test passes. Am I using the test scheduler wrong? What do I need to do in order for the Throttle to be consumed?

Rea answered 19/10, 2015 at 13:27 Comment(0)
B
7

Your code is (almost) perfectly correct, but you're getting tricked by the WhenAnyValue/Skip behavior.

WhenAnyValue will publish an initial value (null) which you're trying to skip using Skip. But because Throttle is running on the TestScheduler, Skip won't be hit till you effectively start the scheduler (AdvanceBy) at which point 2 changes are queued (null and the "test value"), but Throttle will drop the first one, and Skip the second one, hence your subscribe code will never be called.

Many ways to fix that:

  1. add scheduler.AdvanceByMs(1000); before setting the test value (will ensure the initial value gets propagated)

  2. move Skip before the Throttle (doesn't need a scheduler, hence will apply immediately)

(the later is probably the better, as your code is wrongly assuming that it won't get a selection during 500ms after the view model creation, which is unlikely, but not entirely impossible either)

Backed answered 20/10, 2015 at 8:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.