Why does the BackgroundWorker not call the RunWorkerCompleted on the right thread in this unit test?
.net, c#, multithreading
Solution
The behavior is different dues to the different contexts that you are running under.
When you call bw.RunWorkerAsync(), the SynchronizationContext is captured. This is used to dispatch out the RunWorkerCompleted call.
Under WPF it will use DispatcherSynchronizationContext which will marshall the completed call back to the UI thread. Under the test, this marshalling is unnecessary so it remains on the background worker thread.
Problem
The whole point of the backgroundWorker is to update the UI after a time-consuming task. The component works as advertised in my WPF app. However in my test, the callback is not invoked on the calling thread. ``` [Test] public void TestCallbackIsInvokedOnClientThread() { var clientId = Thread.CurrentThread.ManagedThreadId; int callbackThreadId = -1; var manualEvent = new ManualResetEventSlim(false); var someUIControl = new TextBox(); var bw = new BackgroundWorker(); bw.DoWork += (s,e) => e.Result = 5 ; // worker thread bw.RunWorkerCompleted += (s, e) => { try { callbackThreadId = Thread.CurrentThread.ManagedThreadId; //someUIControl.Text = callbackThreadId.ToString(); manualEvent.Set(); } catch (System.Exception ex) { Console.Out.WriteLine(ex.ToString()); } }; bw.RunWorkerAsync(); if (!manualEvent.Wait(5000)) Assert.Fail("no callback"); Assert.AreEqual(clientId, callbackThreadId); } ``` Result Message: Assert.AreEqual failed. Expected:<15>. Actual:<10>. callback not invoked on client Thread What am I missing ? In the Unit Test I see behavior like ``` ------ Run test started ------ MainThread Id =21 Worker Thread Id =9 Callback Thread Id =9 ``` In the Wpf App, this would be ``` MainThread Id =1 Worker Thread Id =14 Callback Thread Id =1 ``` Update: With Justin's answer, made the following changes and now the test passes - Before creating the BackgroundWorker `SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(control.Dispatcher));` - Instead of using a event for signalling between the threads, simulate a message pump . ``` for (int i = 0; i < 3; i++) { control.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { })); Thread.Sleep(50); } ```