Search code examples
c#unit-testingsystem.reactive

How to test object that pushes values to using Reactive Extensions via `Sample` method


I have a class called ValuePusher with a property named Value and receives a dependency of type ValueReceiver, also with a property named Value. The former class arranges to 'push' values to an instance of the latter class via a System.Reactive.Subjects.Subject<int> object. Initially it pushes values one for one, but later it will restrict the amount of values pushed using the Sample method - note the commented-out call to this method in the code below:

public sealed class ValuePusher : IDisposable
{
    private readonly ValueReceiver _receiver;
    private readonly IScheduler _scheduler;
    private readonly Subject<int> _subject;

    private int _value;

    public ValuePusher(ValueReceiver receiver, IScheduler scheduler)
    {
        _receiver = receiver;
        _scheduler = scheduler;

        // Arrange to push values to `receiver` dependency
        _subject = new Subject<int>();
        _subject.ObserveOn(_scheduler)
                //.Sample(TimeSpan.FromMilliseconds(50), _scheduler)
                .SubscribeOn(_scheduler)
                .Subscribe(i => PushCurrentValueToReceiver());
    }

    public int Value
    {
        get => _value;
        set
        {
            _value = value;
            _subject.OnNext(0);
        }
    }

    private void PushCurrentValueToReceiver()
    {
        _receiver.Value = Value;
    }

    public void Dispose()
    {
        _subject?.OnCompleted();
        _subject?.Dispose();
    }
}

public class ValueReceiver
{
    public int Value { get; set; }
}

I write a unit test for the code above, involving a Microsoft.Reactive.Testing.TestScheduler, which passes:

[TestMethod]
[Timeout(1000)]
public void ReceiverReceivesValueFromPusherViaScheduler()
{
    var scheduler = new TestScheduler();
    var receiver = new ValueReceiver();

    using (var pusher = new ValuePusher(receiver, scheduler))
    {
        scheduler.Start();
        pusher.Value = 1;
        scheduler.AdvanceBy(1);
        Assert.AreEqual(1, receiver.Value);
    }
}

However, if uncomment the call to Sample method the test fails to complete and times-out. How can I change the test code or the production code to verify that values are pushed to the receiving object when Sample is in use?

Source code: https://github.com/DanStevens/StackOverflow71409012


Solution

  • The reason why it times out, seems to be the combination of .Sample(...) and scheduler.Start().

    scheduler.Start() tries execute everything that has been scheduled, but I think Sample() keeps scheduling a sample, and thus scheduler.Start() never finishes.

    So if you remove scheduler.Start(), and do this instead:

    ...
    // Following line instead of scheduler.Start(), needed because of the .SubscribeOn(...) call
    scheduler.AdvanceBy(1);
    
    pusher.Value = 1;
    scheduler.AdvanceBy(TimeSpan.FromMilliseconds(50).Ticks);
    Assert.AreEqual(1, receiver.Value);
    ...
    

    it should work with or without the call to .Sample(...).

    [Edit] as per Dan Stevens' comment.