Unit test completes before Observable.Interval subscription has finished doing work (even when mocked)

68 Views Asked by At

I'm new to Reactive Extensions and have a timer setup that fetches some value from an async method every 10 seconds, .

_myInterval = Observable.Interval(TimeSpan.FromSeconds(10), _scheduler.ThreadPool);
_subscription = _myInterval
    .Select(async _ => await FetchSomeValueFromAsyncService())
    .Switch()
    .Subscribe(x => UseValueInAnotherFunction(x));

This works well and does what I want in my app, but I'm running into a minor issue in unit tests.

I have all async code and services mocked using Moq so everything executes immediately, and I am using a TestScheduler to get the interval to tick and run my code. The problem is that sometimes the asserts at the end of my test will fire before my method in Subscribe has completed (even mocked), so the asserts fail because the values I'm testing haven't updated yet.

_scheduler.AdvanceBy(TimeSpan.FromSeconds(10).Ticks);           
_myClass.Result.Should().Be(2);

This makes sense to me because why would my test code wait for anything to complete. As far as it's concerned it just told the timer to tick (which it does).

So how do I get around this? The only way I've found so far is to put a small Task.Delay(50) before my asserts but this feels icky.

1

There are 1 best solutions below

0
Oleg Dok On

For tests it is acceptable to use a variable with spin-wait or TaskCompletionSource to wait for the subscription to finish. Check this:

        var finished = new TaskCompletionSource();
        var myInterval = Observable.Interval(TimeSpan.FromSeconds(1), Scheduler.ThreadPool);
        var subscription = 
            myInterval
                .Take(3)
                .Finally(() => finished.SetResult())
                .Subscribe(Console.WriteLine);
        finished.Task.Wait();