Мы решили эту проблему, просто высмеивая диспетчер за интерфейсом и извлекая интерфейс из нашего контейнера IOC. Вот интерфейс:
public interface IDispatcher
{
void Dispatch( Delegate method, params object[] args );
}
Вот конкретная реализация, зарегистрированная в контейнере IOC для реального приложения
[Export(typeof(IDispatcher))]
public class ApplicationDispatcher : IDispatcher
{
public void Dispatch( Delegate method, params object[] args )
{ UnderlyingDispatcher.BeginInvoke(method, args); }
// -----
Dispatcher UnderlyingDispatcher
{
get
{
if( App.Current == null )
throw new InvalidOperationException("You must call this method from within a running WPF application!");
if( App.Current.Dispatcher == null )
throw new InvalidOperationException("You must call this method from within a running WPF application with an active dispatcher!");
return App.Current.Dispatcher;
}
}
}
А вот макет, который мы добавляем в код во время модульных тестов:
public class MockDispatcher : IDispatcher
{
public void Dispatch(Delegate method, params object[] args)
{ method.DynamicInvoke(args); }
}
У нас также есть вариант MockDispatcher
, который выполняет делегаты в фоновом потоке, но в большинстве случаев это не обязательно