Introduction
This article examines how to write unit tests for a class which uses DispatcherTimer
to implement its functionality. We will review what problems exist around testing code which depends on DispatcherTimer
, and how to overcome those obstacles. The technique used to implement the unit tests has been consolidated into a base class named TestWithActiveDispatcher
, which makes it easy to create your own unit tests using this functionality.
The demo projects
This article is accompanied by a Visual Studio solution which contains three projects. One of the projects, ClassLib, is a DLL which contains the classes that are tested by unit tests. The ConsoleTestApp project is a console app which does not use any unit testing framework. The NUnitTests project is a DLL which contains a unit test built on top of the NUnit 2.4 framework. I included the console-based application for people who do not happen to have NUnit 2.4 installed on their machine. The unit test code is essentially the same between both versions.
If you do not have NUnit installed on your machine, open Solution Explorer in Visual Studio, right click on the NUnitTest project, and select 'Unload Project'. That will prevent you from receiving compiler errors related to that project.
Background
The threading model in the Windows Presentation Foundation (WPF) is based on the concept of a "dispatcher". Every thread has a Dispatcher
object associated with it. Dispatcher
is responsible for policing cross-thread operations and provides a way for threads to marshal method invocations to each other, via its BeginInvoke
and Invoke
methods.
Dispatcher
contains the WPF version of a Windows message queue and message pump. It provides a prioritized message queue, from which the message pump can pull messages and process them. Most WPF classes have an affinity for the thread on which they are instantiated. That thread affinity is based on an association between the object and the thread's Dispatcher
.
WPF has a specialized timer class which is aware of dispatchers, named DispatcherTimer
. When a DispatcherTimer
ticks, it enqueues a message in the message queue of the Dispatcher
with which it is associated. By default a DispatcherTimer
will enqueue its messages with the Dispatcher
of the thread on which it was created. When that Dispatcher
's message pump gets around to it, the timer's "tick message" is dequeued and the event handling method for that timer's Tick
event is invoked. For all of this to work properly the Dispatcher
associated with the DispatcherTimer
must be running; its message pump must be "pumping".
The problem
DispatcherTimer
works like a charm when using it in a WPF application. You usually would never need to know the underlying details of how it interacts with a Dispatcher
. However, once you start using DispatcherTimer
outside of its natural environment (i.e. a WPF application) it suddenly becomes a rather complicated class to work with.
One reasonable scenario in which a DispatcherTimer
might be used outside of a WPF application is if it happens to be running within a unit test. If you have a class which uses DispatcherTimer
to implement its functionality and want to create unit tests for that class, you will find that there are several obstacles to overcome before the unit tests run properly.
Here are several issues related to testing a class whose functionality depends on the use of DispatcherTimer
:
- Threads that run unit tests do not have an active
Dispatcher
by default.
- The method which activates a
Dispatcher
is a blocking call, so you cannot execute any code which follows that method call until the Dispatcher
is shut down. This makes it difficult to start a Dispatcher
and then run the test code which needs an active Dispatcher
.
- The default priority given to a
DispatcherTimer
's tick message in a message queue is so low that the tick messages are never processed by the message pump. This is not a problem in a normal WPF application, but the behavior can be observed in console applications and when running the NUnit GUI app.
- You need to shut down a thread's
Dispatcher
for a unit test method to complete. Once you shut down a thread's Dispatcher
, you cannot restart it or give the thread a new Dispatcher
. Keep in mind that multiple unit test methods might need to run on the same thread.
- Since the code being tested involves the use of a timer, your test code must be able to wait idly so that the object being tested has enough time to complete its work. However, since the
DispatcherTimer
being used runs on the same thread as the code which tests it, you cannot put the thread to sleep or put it into a simple "stand by" loop. If you were to use either of those two approaches, the Dispatcher
's message pump would not be given a chance to process the timer's tick messages (because the thread it runs on would either be sleeping or stuck executing a "stand by" loop).
The solution
As you might imagine, the solution to this problem requires some fancy footwork. I created the TestWithActiveDispatcher
base class to encapsulate the heavy lifting, so that we can simply subclass it and easily make as many unit tests involving DispatcherTimer
s as we need. This section of the article reviews the high-level concepts in TestWithActiveDispatcher
. The 'How it works' section later on shows how the class was implemented.
I think the clearest way to explain how TestWithActiveDispatcher
works is with images and brief blurbs about the images. The following five steps show how it works.
Step 1 � Spawn a worker thread to run the test method
First a worker thread is created and the main thread joins to it, so that the main thread will not execute until the worker thread dies.
Step 2 � Worker thread posts a delayed call to the test method
When the worker thread is up and running, it enqueues a message to its inactive Dispatcher
's message queue. The message is a request to execute the method which contains the test code. At this point the worker thread's Dispatcher
is not running yet, so the message just sits in the queue and waits for the Dispatcher
to eventually process it.
Step 3 � Worker thread starts its Dispatcher
At this point the worker thread tells its Dispatcher
to start running. This activates the message pump, which then processes the message waiting in its queue (the message posted there in the previous step). When the message is processed it causes the test method to be executed. The test method now runs on a thread which has an active Dispatcher
, so it can properly exercise the code which uses a DispatcherTimer
.
Step 4 � DispatcherTimer makes use of the active Dispatcher
Now the code under test is being exercised by the test method, and it creates a DispatcherTimer
which places tick messages into the Dispatcher
's message queue. The Dispatcher
's message pump processes the tick messages and invokes the method which handles the Tick
event. As far as the code being tested is concerned, it may as well be executing in a normal WPF application. It has an active Dispatcher
processing its tick messages and all is well.
Step 5 � Test method completes and the Dispatcher is shut down
Once the test method determines that the test is over, the Dispatcher
is shut down so that the worker thread can die.
Putting TestWithActiveDispatcher to use
In this section we see how to write a class which descends from TestWithActiveDispatcher
, and tests a simple class which uses a DispatcherTimer
. The class under test is called Ticker
. Ticker
is a class which prints "Tick!" to the console window a specified number of times, at a specified time interval. For example, it might be configured to print "Tick!" three times, once every two seconds.
The Ticker
class exposes a public API which looks like this:
interface ITicker
{
void Start();
int Ticks { get; }
}
The Start
method tells the Ticker
to begin printing "Tick!" to the console. The Ticks
property returns the number of times the Ticker
has ticked since the last call to Start
.
Ticker
's constructor requires you to pass in a TickerSettings
object. That class is used to configure the Ticker
instance with various settings. Here is the public API for TickerSettings
:
interface ITickerSettings
{
TimeSpan Interval { get; }
int NumberOfTicks { get; }
DispatcherPriority Priority { get; }
}
The test we want to create will verify that Ticker
honors the Interval
and NumberOfTicks
settings. Here are the setup and test methods, based on the NUnit 2.4 framework:
[SetUp]
public void ConfigureTickerSettings()
{
TimeSpan interval = TimeSpan.FromSeconds(1);
int numberOfTicks = 3;
DispatcherPriority priority = DispatcherPriority.Normal;
_settings = new TickerSettings(interval, numberOfTicks, priority);
}
[Test]
public void TickerHonorsIntervalAndNumberOfTicks()
{
_ticker = null;
base.BeginExecuteTest();
Assert.IsNotNull(_ticker, "_ticker should have been assigned a value.");
Assert.AreEqual(3, _ticker.Ticks);
}
The comment in the setup method points out a very important fact. If you are going to run a DispatcherTimer
in a unit test, you need to be sure to give it a priority higher than the default value of 'Background
'. If you do not do this, the timer tick messages will never be processed. I don't know why this is true, but it's true.
The test method seems rather empty. However, keep in mind that the class our test lives in derives from the TestWithActiveDispatcher
class. When we call BeginExecuteTest
it will eventually result in an invocation of the following overridden method:
protected override void ExecuteTestAsync()
{
Debug.Assert(base.IsRunningOnWorkerThread);
_ticker = new Ticker(_settings);
_ticker.Start();
TimeSpan waitTime = this.CalculateWaitTime();
base.WaitWithoutBlockingDispatcher(waitTime);
base.EndExecuteTest();
}
This is the method which exercises the Ticker
class. It is important to note that the Ticker
instance being tested is created in this method. Since this method executes on a worker thread spawned by TestWithActiveDispatcher
, we need to be sure to create the Ticker
in this method. Doing so ensures that when Ticker
creates its DispatcherTimer
, the correct (and active) Dispatcher
will be associated with it.
After the Ticker
is told to start ticking, we need to wait around until enough time has elapsed for the Ticker
to get its work done. To achieve that we call the WaitWithoutBlockingDispatcher
method, which is inherited from TestWithActiveDispatcher
, and tell it how long to wait. The logic which determines how long to wait looks like this:
TimeSpan CalculateWaitTime()
{
Debug.Assert(base.IsRunningOnWorkerThread);
int numTicks = _settings.NumberOfTicks + 1;
int tickInterval = (int)_settings.Interval.TotalSeconds;
int secondsToWait = numTicks * tickInterval;
TimeSpan waitTime = TimeSpan.FromSeconds(secondsToWait);
return waitTime;
}
One last thing to note is that at the end of the overridden ExecuteTestAsync
method seen above, the EndExecuteTest
method is called. This is a required step because it lets TestWithActiveDispatcher
know that it should shut down the worker thread's Dispatcher
. Once the Dispatcher
is inactive, the test is over and another test method can be run.
How it works
In this section we will see how the TestWithActiveDispatcher
class works. It is not necessary to read this section in order to use the class. This section breaks the implementation down into the same five steps seen in the previous 'The solution' section.
Step 1 � Spawn a worker thread to run the test method
When the subclass wants to run its test code, it needs to call the BeginExecuteTest
method. That method kicks off the whole process by spawning a thread which executes the BeginExecuteTestAsync
method and then waiting for that thread to die. When the worker thread dies, the test is over.
protected void BeginExecuteTest()
{
Debug.Assert(this.IsRunningOnWorkerThread == false);
Thread thread = new Thread(this.BeginExecuteTestAsync);
thread.SetApartmentState(ApartmentState.STA);
thread.Name = WORKER_THREAD_NAME;
thread.Start();
thread.Join();
}
Steps 2 & 3 � Worker thread posts a delayed call to the test method and starts its Dispatcher
At this point the rest of the code we review executes on a worker thread. The BeginExecuteTestAsync
method is responsible for solving the chicken and egg problem of starting the Dispatcher
and executing the test method which requires an active Dispatcher
(remember, Dispatcher.Run
is a blocking call).
void BeginExecuteTestAsync()
{
Debug.Assert(this.IsRunningOnWorkerThread);
NoArgDelegate testMethod = new NoArgDelegate(this.ExecuteTestAsync);
Dispatcher.CurrentDispatcher.BeginInvoke(
DispatcherPriority.Normal, testMethod);
Dispatcher.Run();
}
When the call to Dispatcher.Run
returns that means the Dispatcher
has been shut down and the test method is complete. At that point the worker thread dies and the BeginExecuteTest
method seen in Step 1 is finished as well.
Step 4 � DispatcherTimer makes use of the active Dispatcher
The child class must override ExecuteTestAsync
and put the test code there. That is the correct place to put code which directly or indirectly creates a DispatcherTimer
. When this method is invoked, the worker thread's Dispatcher
is already running and ready to process messages.
protected abstract void ExecuteTestAsync();
Step 5 � Test method completes and the Dispatcher is shut down
At the end of the overridden ExecuteTestAsync
the child class is expected to call EndExecuteTest
. That method shuts down the worker thread's Dispatcher
.
protected void EndExecuteTest()
{
Debug.Assert(this.IsRunningOnWorkerThread);
Dispatcher disp = Dispatcher.CurrentDispatcher;
Debug.Assert(disp.HasShutdownStarted == false);
disp.BeginInvokeShutdown(DispatcherPriority.Normal);
}
The last step � Waiting idly while the test executes
There is one final detail involved with this class. While the code being tested is running TestWithActiveDispatcher
needs to wait around idly, such that it does not prevent the Dispatcher
's message pump from getting a chance to process messages. This involves the use of an old trusty evil hack�DoEvents
.
In case you are not familiar with DoEvents
, just think of it as a way of processing all of the messages in the message queue in one fell swoop. I used this wretched technique here so that the DispatcherTimer
's tick messages would get a chance to be processed by the message pump. If I did not use it, the while
loop seen below would dominate the worker thread until the loop finished.
WPF does not have its own version of DoEvents
but a fellow by the name of Zhou Yong posted a smart way of implementing it on his blog, and I wrapped that code into a utility class named DispatcherHelper
. (See the 'References' section below for a link to the page I'm referring to).
protected void WaitWithoutBlockingDispatcher(TimeSpan waitTime)
{
Debug.Assert(this.IsRunningOnWorkerThread);
DateTime startTime = DateTime.Now;
bool wait = true;
while (wait)
{
DispatcherHelper.DoEvents();
TimeSpan elapsed = DateTime.Now - startTime;
wait = elapsed < waitTime;
}
}
I would not advise using DoEvents
in production code, unless it is absolutely positively necessary. DoEvents
is like applying a severe earthquake to the state of your application. Since this is only test code I'm not too worried about it.
References
Revision History
- July 14, 2007 � Created the article