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 benull
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
What Is the Real Overhead of Try/Catch in C#
Background Color of a Listbox Item (Windows Forms)
Configure JSON.Net to Ignore Datacontract/Datamember Attributes
Using the Null-Conditional Operator on the Left-Hand Side of an Assignment
Linq to SQL Using Group by and Count(Distinct)
How to Draw Directly on the Windows Desktop, C#
How to Change Symbol for Decimal Point in Double.Tostring()
Select Parsed Int, If String Was Parseable to Int
Best Way to Access Com Objects from C#
Icecast 2: Protocol Description, Streaming to It Using C#
Convert Array of Bytes to Bitmapimage
How to Render a Razor View to a String in ASP.NET MVC 3
How to Run a Task on a Custom Taskscheduler Using Await
How to Use JSON.Net for JSON Modelbinding in an MVC5 Project