Content

2/24/2012

Using the WPF Dispatcher in unit tests


I have got a problem in my current WPF project when unit testing with mstest.
In one of my ViewModels i need to use the dispatcher to set something in the view.
While Unit Testing this ViewModel i figured out theAction delegate will never covered.

Unit-Test with Dispatcher and BeginInvoke fails
  1.         [TestMethod()]
  2.         public void TestTheTest1()
  3.         {
  4.             bool myBoolean = false;
  5.             Action theAction = () => myBoolean = true;
  6.             Task.Factory.StartNew(() =>
  7.                 Dispatcher.CurrentDispatcher.BeginInvoke(
  8.                 DispatcherPriority.Normal,
  9.                 theAction));
  10.             Thread.Sleep(500);
  11.             Assert.IsTrue(myBoolean);
  12.         }        

The reason for this behavior is that the dispatcher is not processing any messages when called from the testrunner. A synchronous call (Invoke) still gets executed but no queued ones (BeginInvoke).

Unit-Test with Dispatcher and Invoke succeed
  1.         [TestMethod()]
  2.         public void TestTheTest2()
  3.         {
  4.             bool myBoolean = false;
  5.             Action theAction = () => myBoolean = true;
  6.             Task.Factory.StartNew(() =>
  7.                 Dispatcher.CurrentDispatcher.Invoke(
  8.                 DispatcherPriority.Normal,
  9.                 theAction));
  10.             Thread.Sleep(500);
  11.             Assert.IsTrue(myBoolean);
  12.         }

So i handled this by create a Dispatch helper class for myself. This class handle all Dispatcher calls at runtime and for unit-tests.

There are also other solutions to handle this problem.
You can trigger the processing of the messages manually with a DispatcherFrame.
The changed and working example looks like this:


Solution with DispatcherFrame
  1.         [TestMethod()]
  2.         public void TestTheTest3()
  3.         {
  4.             bool myBoolean = false;
  5.             DispatcherFrame frame = null;
  6.             Action theAction = () =>
  7.                 {
  8.                     myBoolean = true;
  9.                     frame.Continue = true;
  10.                 };
  11.             Task.Factory.StartNew(() =>
  12.                 {
  13.                     frame = new DispatcherFrame();
  14.                     Dispatcher.CurrentDispatcher.BeginInvoke(
  15.                         DispatcherPriority.Normal,
  16.                         theAction);
  17.                     Dispatcher.PushFrame(frame);
  18.                 });
  19.             Thread.Sleep(500);
  20.             Assert.IsTrue(myBoolean);
  21.         }

Also possible is to use a interface for the dispatcher and to have the posibility to mock the dispatcher.At runtime you can pulling in the interface from your IOC container.

Dispatcher interface
  1. public interface IDispatcher
  2. {
  3.     void Dispatch( Delegate method, params object[] args );
  4. }

1 comment:

  1. Good post! This helped me sort out dispatcher issues on WPF testing in MSTEST.
    If you change frame.Continue to false, and Thread.Sleep to task.Wait() you can use this for long running tests.
    You can extend this further by creating a helper method that takes an action as a parameter, and then use AOP to neatly perform tests using the dispatcher (just need to set a method attribute)

    ReplyDelete