Reputation: 3234
I've tried various methods from other SO questions over the last week and this is the best approach I found yet, but I do not know how to unit test this with NUnit.
private void VerifyDispatcherThread()
{
var context = SynchronizationContext.Current;
if (context != null && context is DispatcherSynchronizationContext)
{
Log.For(this).Info("WARNING! - Method Should Not Be Called From Dispatcher Thread.");
if (DispatcherThreadSafetyEnabled)
{
throw new ThreadStateException("Method Should Not Be Called From Dispatcher Thread.");
}
}
}
HERE IS OUR TEST (Not Working)
[Test]
public void Should_throw_exception_when_called_from_dispatcher()
{
// Arrange
var called = false;
Boolean? success = null;
var httpClient = new HttpClient(HttpTimeOut);
// This emulates WPF dispatcher thread
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
// Act
dispatcher.BeginInvoke(delegate()
{
try
{
ConfigurationManager.AppSettings.Set("DispatcherThreadSafetyEnabled", "true");
httpClient.Post("http://Foo", "content xml");
success = true;
}
catch
{
success = false;
}
finally
{
ConfigurationManager.AppSettings.Set("DispatcherThreadSafetyEnabled", "false");
}
called = true;
});
// this part is a bit ugly
// spinlock for a bit to give the other thread a chance
var timeout = 0;
while (!called && timeout++ < 1000)
Thread.Sleep(1);
// Assert
Assert.IsNotNull(success, "Method was never invoked");
Assert.IsFalse(success.Value, "Expected to fail");
}
DispatcherThreadSafetyEnabled is a app.config setting we have so we can toggle only logging or throwing exceptions.
What we are trying to do is verify the calling method isn't using the GUI thread when invoked. It appears to be working but we are having a hard time trying to unit test this.
Here's hoping someone has done this and can help us out with our challenge.
Upvotes: 0
Views: 285
Reputation: 4149
The current SynchronizationContext
is set to different implementations by different frameworks (Winforms, WPF, etc).
Quoting from this MSDN article:
WindowsFormsSynchronizationContext (System.Windows.Forms.dll: System.Windows.Forms) Windows Forms apps will create and install a WindowsFormsSynchronizationContext as the current context for any thread that creates UI controls.
DispatcherSynchronizationContext (WindowsBase.dll: System.Windows.Threading) WPF and Silverlight applications use a DispatcherSynchronizationContext, which queues delegates to the UI thread’s Dispatcher with “Normal” priority. This SynchronizationContext is installed as the current context when a thread begins its Dispatcher loop by calling Dispatcher.Run.
Since you are only testing for not null and the type of the SynchronizationContext
, try setting current SynchronizationContext
to an instance of DispatcherSynchronizationContext
by calling SynchronizationContext.SetSynchronizationContext
from your unit tests. Sample code below:
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
Please note this is not a recommended in a Winforms/WPF application though.
Upvotes: 1