Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / WPF
Article

How to Test a Class Which Uses DispatcherTimer

Rate me:
Please Sign up or sign in to vote.
5.00/5 (20 votes)
14 Jul 2007CPOL11 min read 129.7K   1.2K   44   20
Demonstrates how to create unit tests for a class which uses a DispatcherTimer.

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:

  1. Threads that run unit tests do not have an active Dispatcher by default.
  2. 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.
  3. 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.
  4. 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.
  5. 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 DispatcherTimers 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

Screenshot - step1.png

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

Screenshot - step2.png

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

Screenshot - step3.png

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

Screenshot - step4.png

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

Screenshot - step5.png

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:

C#
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:

C#
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:

C#
/// <summary>
/// Initializes data used in the test.
/// </summary>
[SetUp]
public void ConfigureTickerSettings()
{
  TimeSpan interval = TimeSpan.FromSeconds(1);
  int numberOfTicks = 3;
  DispatcherPriority priority = DispatcherPriority.Normal;

  // VERY IMPORTANT!!!
  // For the DispatcherTimer to tick when it is not running
  // in a normal WPF application, you must give it a priority
  // higher than 'Background' (which is the default priority).
  // In this demo we give it a priority of '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:

C#
protected override void ExecuteTestAsync()
{
  Debug.Assert(base.IsRunningOnWorkerThread);

  // Note: The object which creates a DispatcherTimer
  // must create it with the Dispatcher for the worker
  // thread.  Creating the Ticker on the worker thread
  // ensures that its DispatcherTimer uses the worker
  // thread's Dispatcher.
  _ticker = new Ticker(_settings);

  // Tell the Ticker to start ticking.
  _ticker.Start();

  // Give the Ticker some time to do its work.
  TimeSpan waitTime = this.CalculateWaitTime();
  base.WaitWithoutBlockingDispatcher(waitTime);

  // Let the base class know that the test is over
  // so that it can turn off the worker thread's
  // message pump.
  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:

C#
TimeSpan CalculateWaitTime()
{
  Debug.Assert(base.IsRunningOnWorkerThread);

  // Calculate how much time the Ticker needs to perform
  // all of it's ticks.  Add some extra time to make sure
  // it does not tick more than it should.
  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.

C#
/// <summary>
/// Calling this method causes the ExecuteTestAsync override to be
/// invoked in the child class.  Invoke this method to initiate the
/// asynchronous testing.
/// </summary>
protected void BeginExecuteTest()
{
  Debug.Assert(this.IsRunningOnWorkerThread == false);

  // Run the test code on an STA worker thread
  // and then wait for that thread to die before
  // this method continues executing.  We use an
  // STA thread because many WPF classes require it.
  // We must do this work on a worker thread because
  // once a thread's Dispatcher is shut down it cannot
  // be run again.
  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).

C#
void BeginExecuteTestAsync()
{
  Debug.Assert(this.IsRunningOnWorkerThread);

  NoArgDelegate testMethod = new NoArgDelegate(this.ExecuteTestAsync);

  // The call to ExecuteTestAsync must be delayed so
  // that the worker thread's Dispatcher can be started
  // before the method executes.  This is needed because
  // the Dispatcher.Run method does not return until the
  // Dispatcher has been shut down.
  Dispatcher.CurrentDispatcher.BeginInvoke(
    DispatcherPriority.Normal, testMethod);

  // Start the worker thread's message pump so that
  // queued messages are processed.
  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.

C#
/// <summary>
/// Derived classes must override this method to implement their test logic.
/// Note: this method is invoked on a worker thread with active Dispatcher.
/// </summary>
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.

C#
/// <summary>
/// Stops the worker thread's message pump.  Derived classes
/// should call this method at the end of their ExecuteTestAsync
/// override to shut down the worker thread's Dispatcher.
/// Note: this method must be invoked from the worker thread.
/// </summary>
protected void EndExecuteTest()
{
  Debug.Assert(this.IsRunningOnWorkerThread);

  Dispatcher disp = Dispatcher.CurrentDispatcher;
  Debug.Assert(disp.HasShutdownStarted == false);

  // Kill the worker thread's Dispatcher so that the
  // message pump is shut down and the thread can die.
  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).

C#
/// <summary>
/// Pauses the worker thread for the specified duration, but allows
/// the Dispatcher to continue processing messages in its message queue.
/// Note: this method must be invoked from the worker thread.
/// </summary>
/// <param name="waitTime">The amount of time to pause.</param>
protected void WaitWithoutBlockingDispatcher(TimeSpan waitTime)
{
  Debug.Assert(this.IsRunningOnWorkerThread);

  // Save the time at which the wait began.
  DateTime startTime = DateTime.Now;
  bool wait = true;
  while (wait)
  {
    // Call DoEvents so that all of the messages in
    // the worker thread's Dispatcher message queue
    // are processed.
    DispatcherHelper.DoEvents();

    // Check if the wait is over.
    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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Josh creates software, for iOS and Windows.

He works at Black Pixel as a Senior Developer.

Read his iOS Programming for .NET Developers[^] book to learn how to write iPhone and iPad apps by leveraging your existing .NET skills.

Use his Master WPF[^] app on your iPhone to sharpen your WPF skills on the go.

Check out his Advanced MVVM[^] book.

Visit his WPF blog[^] or stop by his iOS blog[^].

See his website Josh Smith Digital[^].

Comments and Discussions

 
QuestionCan't get it working for my COM events Pin
are_all_nicks_taken_or_what13-Dec-11 22:01
are_all_nicks_taken_or_what13-Dec-11 22:01 
GeneralMy vote of 1 Pin
battinisuresh5-Sep-11 2:31
battinisuresh5-Sep-11 2:31 
GeneralThanks! Pin
alexander ignatov4-Dec-09 3:38
alexander ignatov4-Dec-09 3:38 
GeneralHi Josh Pin
Yogesh Jagota9-Nov-08 9:29
Yogesh Jagota9-Nov-08 9:29 
GeneralRe: Hi Josh Pin
Josh Smith9-Nov-08 17:08
Josh Smith9-Nov-08 17:08 
GeneralRe: Hi Josh Pin
Yogesh Jagota9-Nov-08 17:21
Yogesh Jagota9-Nov-08 17:21 
GeneralRe: Hi Josh Pin
Yogesh Jagota10-Nov-08 3:12
Yogesh Jagota10-Nov-08 3:12 
Never mind. I solved the problem by running the Loading Window in a different thread. Smile | :)
GeneralRe: Hi Josh Pin
Josh Smith10-Nov-08 3:19
Josh Smith10-Nov-08 3:19 
GeneralAsserting inside ExecuteTestAsync Pin
John Mairs29-Jul-08 3:54
John Mairs29-Jul-08 3:54 
GeneralRe: Asserting inside ExecuteTestAsync Pin
John Mairs29-Jul-08 4:39
John Mairs29-Jul-08 4:39 
GeneralIt appears you need one class per [Test] Pin
John Mairs8-Jul-08 11:51
John Mairs8-Jul-08 11:51 
GeneralRe: It appears you need one class per [Test] Pin
Josh Smith8-Jul-08 11:54
Josh Smith8-Jul-08 11:54 
GeneralRe: It appears you need one class per [Test] Pin
John Mairs10-Jul-08 3:24
John Mairs10-Jul-08 3:24 
GeneralRe: It appears you need one class per [Test] Pin
Josh Smith10-Jul-08 3:27
Josh Smith10-Jul-08 3:27 
AnswerRe: It appears you need one class per [Test] Pin
Member 146769017-Dec-19 4:13
Member 146769017-Dec-19 4:13 
GeneralAmazing, as always Pin
0xfded17-Jul-07 8:21
0xfded17-Jul-07 8:21 
GeneralRe: Amazing, as always Pin
Josh Smith17-Jul-07 8:38
Josh Smith17-Jul-07 8:38 
GeneralRe: Amazing, as always Pin
Sacha Barber18-Jul-07 1:37
Sacha Barber18-Jul-07 1:37 
GeneralGRRR to good Pin
Sacha Barber15-Jul-07 21:35
Sacha Barber15-Jul-07 21:35 
GeneralRe: GRRR to good Pin
Josh Smith16-Jul-07 2:42
Josh Smith16-Jul-07 2:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.