Bending Time with the Reactive Extensions
The latest releases of the Reactive Extensions for .Net include an abstract VirtualScheduler and a concrete implementation called TestScheduler.
So now it’s possible test time dependent code without relying on the passage of time (or tide).
Here’s a sample of code that would take 3 days to complete in the real
[Fact(Timeout = 1000)] public void TestScheduler() { List<long> actual = new List<long>(); Observable.Interval(TimeSpan.FromDays(1), _testSched) .Take(3) .Subscribe(actual.Add); _testSched.Run(); Assert.Equal(new[] { 0L, 1, 2 }, actual.ToArray()); }
Notice that I didn’t use a blocking call, such as
.Take(3).ToEnumerable().ToArray()
to obtain a the values from the interval. The TestScheduler runs on the current thread, and as a result blocking calls never complete.
Here’s another example where we run for a specific duration. Usefull when testing Observables that never end
[Fact] public void TestOneElementSlidingWindow() { List<SlidingWindow<Timestamped<int>>> actual = new List<SlidingWindow<Timestamped<int>>>(); var oneBeat = Observable.Return<int>(1, _testSched).Timestamp(_testSched); var sWindow = oneBeat.ToSlidingWindow(_oneSecond, _oneSecond, _testSched); sWindow.Subscribe(slw => actual.Add(slw)); _testSched.RunTo(_testSched.FromTimeSpan(TimeSpan.FromSeconds(3))); Assert.Equal(2, actual.Count); Assert.Equal(1, actual[0].Added.Count()); Assert.Equal(1, actual[0].Current.Count()); Assert.Equal(0, actual[0].Removed.Count()); Assert.Equal(0, actual[1].Added.Count()); Assert.Equal(0, actual[1].Current.Count()); Assert.Equal(1, actual[1].Removed.Count()); }
Code samples updated at http://code.msdn.microsoft.com/RxDemos
Also - Jeffrey van Gogh promises more to come on #c9