Unit test IObservable<T> with ObserveOnDispatcher
Asked Answered
W

3

5

I need to test piece of code

        var watcher = new FakeIFileSystemWatcher();
        watcher.FilesToBeImported
            .ObserveOnDispatcher()
            .Subscribe(list.Add);

so I created this little unit test but I can't make it pass cause list.Count is always 0

    [Test]
    public void Foo()
    {
        var list = new List<string>();

        var watcher = new FakeIFileSystemWatcher();
        watcher.FilesToBeImported
            .ObserveOnDispatcher()
            .Subscribe(list.Add);

        Task task = Task.Factory.StartNew(() =>
        {                
            watcher.AddFile("cc");
            watcher.AddFile("cc");
            watcher.AddFile("cc");
        }, TaskCreationOptions.LongRunning);
        Task.WaitAll(task);

        Assert.AreEqual(3, list.Count);
    }

if I comment out the method

            .ObserveOnDispatcher()

it pass but how can I get it working also with ObserveOnDispatcher() ?

Wainscoting answered 4/7, 2012 at 15:16 Comment(3)
What is the value of list.Count at failure?Clarissa
I solved using the class DispatcherUtil I found here #1107381Wainscoting
I just want to point out that by having concurrency (implicit or explicit) in your unit tests, you really are limited in what you can do. Here you have both Tasks and a Dispatcher that you have provided no 'seams' in which you can substitute them for a test double (mock/stub). Just as (I assume) you inject a real implementation of IFileSystemWatcher in your prod code, you would inject a SchedulerProvider that gave you real concurreny in prod but test schedulers in you unit tests. Much faster unit tests, easier to test other things like timeouts etc too.Fornication
W
1

I solved using the class DispatcherUtil I found here Using the WPF Dispatcher in unit tests

now my code is the following

    [Test]
    public void Foo()
    {
        var list = new List<string>();

        var watcher = new FakeIFileSystemWatcher();
        watcher.FilesToBeImported
            .ObserveOnDispatcher()
            .Subscribe(list.Add);

        Task task = Task.Factory.StartNew(() =>
        {
            watcher.AddFile("cc");
            watcher.AddFile("cc");
            watcher.AddFile("cc");
            watcher.AddFile("cc");
        }, TaskCreationOptions.LongRunning);
        Task.WaitAll(task);
        DispatcherUtil.DoEvents();
        Assert.AreEqual(4, list.Count);
    }

and it works like a charm

Wainscoting answered 5/7, 2012 at 7:8 Comment(0)
F
7

If you use ObserveOnDispatcher you create a dependency to the "dispatcher" which means that you need a window and a message loop. To get around this problem in a unit test you can instead use the ObserveOn method that uses a scheduler and then use dependency injection to inject the correct scheduler. For unit testing you could use Scheduler.Immediate and for the actual application you could use DispatcherScheduler.Instance. Notice that there also exists a TestScheduler which is really useful for running unit tests in virtual time.

Folie answered 4/7, 2012 at 15:41 Comment(3)
Thanks for you reply. this line of code watcher.FilesToBeImported.ObserveOnDispatcher().Subscribe(list.Add); is in my system under test so I can't change it just for the testWainscoting
@scott4dev: My point was that you sometimes have to modify your system under test to make it unit testable. E.g. if you access a database you have to create an interface for the database allowing you to stub the database access etc. Your solution avoid this of course.Folie
you are right, sometimes have to modify your system under test to make it unit testable but in my opinion this not the case. I prefer to use DispatcherUtil.DoEvents(); in my tests and let my SUT in the original stateWainscoting
W
1

I solved using the class DispatcherUtil I found here Using the WPF Dispatcher in unit tests

now my code is the following

    [Test]
    public void Foo()
    {
        var list = new List<string>();

        var watcher = new FakeIFileSystemWatcher();
        watcher.FilesToBeImported
            .ObserveOnDispatcher()
            .Subscribe(list.Add);

        Task task = Task.Factory.StartNew(() =>
        {
            watcher.AddFile("cc");
            watcher.AddFile("cc");
            watcher.AddFile("cc");
            watcher.AddFile("cc");
        }, TaskCreationOptions.LongRunning);
        Task.WaitAll(task);
        DispatcherUtil.DoEvents();
        Assert.AreEqual(4, list.Count);
    }

and it works like a charm

Wainscoting answered 5/7, 2012 at 7:8 Comment(0)
R
0

You can try to invoke your method like this:

var dispatcher = Application.Current != null ? Application.Current.Dispatcher : Dispatcher.CurrentDispatcher;

 dispatcher.Invoke((Action)(() => YourMethodToTest());
Reasoning answered 23/9, 2013 at 6:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.