I\'m having trouble getting the Dispatcher to run a delegate I\'m passing to it when unit testing. Everything works fine when I\'m running the program, but, during a unit te
You can unit test using a dispatcher, you just need to use the DispatcherFrame. Here is an example of one of my unit tests that uses the DispatcherFrame to force the dispatcher queue to execute.
[TestMethod]
public void DomainCollection_AddDomainObjectFromWorkerThread()
{
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
DispatcherFrame frame = new DispatcherFrame();
IDomainCollectionMetaData domainCollectionMetaData = this.GenerateIDomainCollectionMetaData();
IDomainObject parentDomainObject = MockRepository.GenerateMock<IDomainObject>();
DomainCollection sut = new DomainCollection(dispatcher, domainCollectionMetaData, parentDomainObject);
IDomainObject domainObject = MockRepository.GenerateMock<IDomainObject>();
sut.SetAsLoaded();
bool raisedCollectionChanged = false;
sut.ObservableCollection.CollectionChanged += delegate(object sender, NotifyCollectionChangedEventArgs e)
{
raisedCollectionChanged = true;
Assert.IsTrue(e.Action == NotifyCollectionChangedAction.Add, "The action was not add.");
Assert.IsTrue(e.NewStartingIndex == 0, "NewStartingIndex was not 0.");
Assert.IsTrue(e.NewItems[0] == domainObject, "NewItems not include added domain object.");
Assert.IsTrue(e.OldItems == null, "OldItems was not null.");
Assert.IsTrue(e.OldStartingIndex == -1, "OldStartingIndex was not -1.");
frame.Continue = false;
};
WorkerDelegate worker = new WorkerDelegate(delegate(DomainCollection domainCollection)
{
domainCollection.Add(domainObject);
});
IAsyncResult ar = worker.BeginInvoke(sut, null, null);
worker.EndInvoke(ar);
Dispatcher.PushFrame(frame);
Assert.IsTrue(raisedCollectionChanged, "CollectionChanged event not raised.");
}
I found out about it here.
If your goal is to avoid errors when accessing DependencyObject
s, I suggest that, rather than playing with threads and Dispatcher
explicitly, you simply make sure that your tests run in a (single) STAThread
thread.
This may or may not suit your needs, for me at least it has always been enough for testing anything DependencyObject/WPF-related.
If you wish to try this, I can point you to several ways to do this :
[RequiresSTA]
attribute that can target test methods or classes. Beware though if you use an integrated test runner, as for example the R#4.5 NUnit runner seems to be based on an older version of NUnit and cannot use this attribute.[STAThread]
thread with a config file, see for example this blog post by Chris Headgate.[STAThread]
thread to run your test on.I'm using MSTest
and Windows Forms
technology with MVVM paradigm.
After trying many solutions finally this (found on Vincent Grondin blog) works for me:
internal Thread CreateDispatcher()
{
var dispatcherReadyEvent = new ManualResetEvent(false);
var dispatcherThread = new Thread(() =>
{
// This is here just to force the dispatcher
// infrastructure to be setup on this thread
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => { }));
// Run the dispatcher so it starts processing the message
// loop dispatcher
dispatcherReadyEvent.Set();
Dispatcher.Run();
});
dispatcherThread.SetApartmentState(ApartmentState.STA);
dispatcherThread.IsBackground = true;
dispatcherThread.Start();
dispatcherReadyEvent.WaitOne();
SynchronizationContext
.SetSynchronizationContext(new DispatcherSynchronizationContext());
return dispatcherThread;
}
And use it like:
[TestMethod]
public void Foo()
{
Dispatcher
.FromThread(CreateDispatcher())
.Invoke(DispatcherPriority.Background, new DispatcherDelegate(() =>
{
_barViewModel.Command.Executed += (sender, args) => _done.Set();
_barViewModel.Command.DoExecute();
}));
Assert.IsTrue(_done.WaitOne(WAIT_TIME));
}
I solved this problem by creating a new Application in my unit test setup.
Then any class under test which access to Application.Current.Dispatcher will find a dispatcher.
Because only one Application is allowed in an AppDomain I used the AssemblyInitialize and put it into its own class ApplicationInitializer.
[TestClass]
public class ApplicationInitializer
{
[AssemblyInitialize]
public static void AssemblyInitialize(TestContext context)
{
var waitForApplicationRun = new TaskCompletionSource<bool>()
Task.Run(() =>
{
var application = new Application();
application.Startup += (s, e) => { waitForApplicationRun.SetResult(true); };
application.Run();
});
waitForApplicationRun.Task.Wait();
}
[AssemblyCleanup]
public static void AssemblyCleanup()
{
Application.Current.Dispatcher.Invoke(Application.Current.Shutdown);
}
}
[TestClass]
public class MyTestClass
{
[TestMethod]
public void MyTestMethod()
{
// implementation can access Application.Current.Dispatcher
}
}