Testing Asynchronous WPF Application with NUnit

Volodymyr Shtenovych News RSS feedNews RSS feed

Testing Asynchronous WPF Application with NUnit

Developing applications that need asynchronous execution is usually much more complicated then ones do not need it. Unfortunately, automated testing of such applications became even harder. Some time ago I experienced difficulties with testing asynchronous Google App Engine service that uses Task Queue. This time it’s WPF desktop application that performs most of its job in background PTL (Parallel Task Library) tasks. Disregarding not-important details I can describe it in the following way:

  1. NET Framework 4.0 desktop application
  2. UI made with WPF (it’s optional, may be created by controller at some point, binded to model and then disposed)
  3. Model detached from the UI. Parts of Model are DependencyObject and ObservableCollection when it’s needed.
  4. Main Application Controller starts in the main thread and runs all other stuff as PTL Tasks.
  5. Main Controller creates, maintains and synchronizes Model with server-side and file system - all is asynchronous and may be performing at the same time.

How was I going to test it?

  1. NUnit
  2. Each test creates an instance of Main Controller, mocking required part of services
  3. Then calling Main Controller methods (or it’s sub-controllers) to perform some part of business logic.Most of methods of Main Controller is asynchronous and exits
  4. immediately returning Task (or Task[]) being executed
  5. Test initiates one or few of such tasks and then it is supposed to wait until all of them  are done and verify result.

Everything was fine in a theory, but the problem is that if you want your model to be accesible in many threads which are different from the main one you need to write code like this in C#:

 

   1:  public static readonly DependencyProperty MyPropProperty = DependencyProperty.Register("MyProperty", typeof(bool), typeof(MyModel));
   2:   
   3:  public bool MyProp
   4:  {
   5:             get
   6:             {
   7:                         (bool) (Dispatcher.CheckAccess()
   8:                                  ? GetValue(MyPropProperty)
   9:                                  : Dispatcher.Invoke((Func<DependencyProperty, object>) GetValue, MyPropProperty));
  10:             }
  11:             set
  12:             {
  13:                     if (Dispatcher.CheckAccess())
  14:                             SetValue(MyPropProperty, value);
  15:                     else
  16:                             Dispatcher.Invoke((Action<DependencyProperty, object>) SetValue, MyPropProperty, value);                   
  17:             }
  18:   }

The code is pretty strait-forward and it works good in application, but when I tried to run it under NUnit test Dispatcher.Invoke just hangs because there was no synchronization context set.

I hate any kind of IF-WE-ARE-UNDER-TEST condition so I wondered is there simple solution to be able to run controller that uses such a model. And naturally the solution should provide ability to wait while Tasks are executing to verify result of them.

At this point the only acceptable idea I got is to create DispatcherFrame before each test in the testing thread (thread the Main Controller and Model is creating in) and provide a method that pushes the frame to the testing thread dispatcher and then waits while certain tasks are done. The test method should create and initialize controller, run it’s methods getting the running Tasks as a result (actually all tasks will be paused as soon as their touch Dispatcher and they will be waiting for dispatcher frame for infinite period of time), wait while they are performing with the help of the method described above and check the result. The code looks like this in C#:

   1:  private DispatcherFrame _dispatcherFrame;
   2:   
   3:  [SetUp]
   4:  public void BeforeTest()
   5:  {
   6:   
   7:      _dispatcherFrame = new DispatcherFrame();
   8:  }
   9:   
  10:  private static readonly TimeSpan WaitTimeout = TimeSpan.FromSeconds(5);
  11:   
  12:  protected void WaitFor(params Task[] tasks)
  13:  {
  14:      bool timedOut = false;
  15:      _dispatcherFrame.Continue = true;
  16:   
  17:      new Task(() =&gt;
  18:          {
  19:              timedOut = !Task.WaitAll(tasks, WaitTimeout);
  20:                 _dispatcherFrame.Continue = false;
  21:          }).Start();
  22:   
  23:          Dispatcher.PushFrame(_dispatcherFrame);
  24:   
  25:      if (timedOut)
  26:          throw new InvalidOperationException(&quot;Waiting timeout&quot;);
  27:  }
  28:   
  29:  [Test]
  30:  public void MyTest()
  31:  {
  32:   
  33:      var mocks = /* configure mocks */
  34:   
  35:          using (controller = new MainController(mocks))
  36:          {
  37:              Task[] firstBunchOfTasks =
  38:                             new Task[]
  39:                         {
  40:                             controller.StartFoo(),
  41:                             controller.StartDoo()
  42:                         };
  43:   
  44:                 WaitFor(firstBunchOfTasks);
  45:   
  46:                     // assert something at this point
  47:   
  48:                 Task someOtherJob = controller.StartJob();
  49:   
  50:                 Task fooAgain = controller.StartFoo();
  51:   
  52:                     WaitFor(someOtherJob, fooAgain);
  53:   
  54:                 // verify final result
  55:             }
  56:  }

Such solution works fine for me but if you know more elegant approach or think it can be done in a completely different way, please share you thoughts with me.

Me in Twitter
Brainbench test C# 4.0
Volodymyr Shtenovych