Testing Asynchronous WPF Application with NUnit |
|
Testing Asynchronous WPF Application with NUnitDeveloping 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:
How was I going to test it?
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. 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(() => 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("Waiting timeout"); 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. |
|