Using the Wpf Dispatcher in Unit Tests

Using the WPF Dispatcher in unit tests

By using the Visual Studio Unit Test Framework you don’t need to initialize the Dispatcher yourself. You are absolutely right, that the Dispatcher doesn’t automatically process its queue.

You can write a simple helper method “DispatcherUtil.DoEvents()” which tells the Dispatcher to process its queue.

C# Code:

public static class DispatcherUtil
{
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}

private static object ExitFrame(object frame)
{
((DispatcherFrame)frame).Continue = false;
return null;
}
}

You find this class too in the WPF Application Framework (WAF).

Correct method for using the WPF Dispatcher in unit tests

Since the dispatcher is problematic in unit tests, my solution would be to break your view-model's dependency on the dispatcher. I assume that currently you have hard coded references like:

Dispatcher.CurrentDispatcher.BeginInvoke(..

The dispatcher is an external dependency and shouldn't be part of your unit tests - we can assume the dispatcher works.

I would use dependency injection (either poor mans, Unity, etc).
Create a suitable interface representing the dispatcher.
Create a real implementation which wraps the real dispatcher.
Create a fake implementation which uses Action.BeginInvoke.
In the fake you record all IAsyncResults returned to calls to BeginInvoke.

Then have a helper method which would wait for all calls to completed which you can use in your test to wait for completion.

Or have a view model base class which does the same thing. Calls the dispatcher normally but can be directed to call a fake during tests.

How to write a WPF async unit test for a method that uses Application.Current.Dispatcher.Invoke?

I'd say that you should hide the dispatching behind an interface and mock it in the unit tests:

interface IDispatcher
{
void Dispatch(Action action);
}

You can easily mock this in your tests and expect to those dispatched calls.

An implementation which uses the real dispatcher and can be used by your app:

public class Dispatcher : IDispatcher
{
public void Dispatch(Action action)
{
Application.Current.Dispatcher.Invoke(action);
}
}

WPF dispatcher never executes action when running UI tests

Assuming the view model method is called in the UI thread of your application, the following code modification should eliminate the need for using a Dispatcher:

public async Task RefreshControls()
{
Parent.IsSoftBusy = true;

if (ControlsList == null)
{
ControlsList = new ObservableCollection<DataprepControl>();
}

var updatedControls = await Task.Run(() => _getControls());

_handleUpdatedControl(updatedControls);

Parent.IsSoftBusy = false;
}

Unit Testing with Application.Current.Dispatcher

The compile error is due to the fact that the constructor you're trying to use is in fact private (and you're calling it with a an argument that it doesn't take)

Looking at the documentation of Dispatcher.Current, it says:

Gets the for the
thread currently executing and creates a new if one is not already
associated with the thread.

So, you can use:

  • _rootDispatcher = Dispatcher.CurrentDispatcher;

resulting in a Dispatcher instance.

As an optimization you can just use Dispatcher.CurrentDispatcher as 'RootDispatcher' all the time. No matter if Application.Current exists.

If you hit this code in both production and test, you'll need to make a distinction between these scenarios. Use the Application's Dispatcher in production, and use 'any' Dispatcher (Dispatcher.Current) in unit tests, because the Application is not there.

WPF component in unit testing

dispatcher = System.Windows.Threading.Dispatcher.FromThread(newWindowThread);

This grabs the dispatcher for this particular thread, from there one one can do

  dispatcher.Invoke(() =>
{
/*here you can access the plotter */
});

Unit testing a class used in multi-threaded WPF application

Thanks to all who responded, your feedback was much appreciated.

Here is what I ended up doing:

I created a private variable in my UICommsManager class:

private SynchronizationContext _MessageHandlerContext = null;

and initialized this in the constructor of the UICommsManager.
I then updated my message handlers to use the new _MessageHandlerContext instead of the Dispatcher:

public async Task<bool> Set_Listing_Records_ResponseHandler(string responseChannelSuffix, Action<List<AIDataSetListItem>> successHandler, Action<Exception> errorHandler)
{
// Subscribe to Query Response Channel and Wire up Handler for Query Response
await this.ConnectAsync();
return await this.SubscribeTo_QueryResponseChannelAsync(responseChannelSuffix, new FayeMessageHandler(delegate (FayeClient client, FayeMessage message) {
_MessageHandlerContext.Post(delegate {
try
{
...
}
catch (Exception e)
{
...
}
}, null);
}));
}

When used from the UI the UICommsManager class gets SynchronizationContext.Current passed into the constructor.

I updated my Unit Test to add the following private variable:

private SynchronizationContext _Context = null;

And the following method which initializes it:

#region Test Class Initialize

[TestInitialize]
public void TestInit()
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
_Context = SynchronizationContext.Current;
}

#endregion

And then the UICommsManager gets the _Context variable passed into it's constructor from the Test Method.

Now it works in both scenarios, when called by WPF, and when called by Unit Test.

MVVM light Dispatcher unit tests

Unfortunately situation is as a result of an initial design issue which made the code base difficult to unit test. The difficulty in creating unit tests for code is directly related to how well the code is designed. The article you mentioned in your post is what you would need to do to make accessing the dispatcher mock-able as it (the dispatcher) is an implementation concern associated with the UI thread which will not be available during your unit tests. Hence the lock on Invoke

To quote the article you mentioned:

We are unable to test the code that uses App.Current.Dispatcher (since
App.Current will be null during unit tests execution).

A possible solution would be to create an interface IDispatcher and a
wrapper around App.Current.Dispatcher that implements that interface.

public interface IDispatcher {
void Invoke(Action action);
void BeginInvoke(Action action);
}

public class DispatcherWrapper : IDispatcher {
Dispatcher dispatcher;

public DispatcherWrapper() {
dispatcher = Application.Current.Dispatcher;
}
public void Invoke(Action action) {
dispatcher.Invoke(action);
}

public void BeginInvoke(Action action) {
dispatcher.BeginInvoke(action);
}
}

How to invoke WPF Dispatcher in Nunit?

I haven't used nUnit to write unit tests before, but this is a common problem with VS unit tests. What can end up happening is that each test uses a different dispatcher and WPF requires you to use the same dispatcher. To get around this, create a static class to cache the Dispatcher and then invoke everything through it.



Related Topics



Leave a reply



Submit