WindowRunner helps you launch a window on a separate thread and then stuff your content into it:

namespace UnitTestingAvalon
{
    using System;

    public class WindowRunner
    {
        #region Constructor
        internal WindowRunner(RetrieveWindow windowCallback, CreateContent contentCreator, object contentInformation)
       
{
           
if (windowCallback == null{ throw new ArgumentNullException("windowCallback"); }
           
this.WindowCallback = windowCallback;

           
if (contentCreator == null) { throw new ArgumentNullException("contentCreator"); }
           
this.ContentCreator = contentCreator;
           
this.ContentInformation = contentInformation;
       
}
        #endregion Constructor

        #region WindowCallback
        internal
delegate void RetrieveWindow(System.Windows.Window Window);
       
private RetrieveWindow WindowCallback { get { return this.windowCallback; } set { this.windowCallback = value; } }
       
private RetrieveWindow windowCallback;
        #region WindowCallback

        #region ContentCreator
        internal
delegate System.Windows.FrameworkElement CreateContent(object contentInformation);
        
private CreateContent ContentCreator { get { return this.contentCreator; } set { this.contentCreator = value; } }
        
private CreateContent contentCreator;
        
#endregion ContentCreator

        
#region ContentInformation
        
private object ContentInformation { get { return this.contentInformation; } set { this.contentInformation = value; } }
        
private object contentInformation;
        
#endregion ContentInformation

        
#region Run
        
internal void Run()
        
{
            
System.Windows.Window avalonWindow = new System.Windows.Window();
            
avalonWindow.SizeToContent = System.Windows.SizeToContent.WidthAndHeight;
            
avalonWindow.Content = this.ContentCreator(this.ContentInformation);
            
this.WindowCallback(avalonWindow);
            
avalonWindow.ShowDialog();
        
}
        
#endregion Run
    
}
}

The content creator method might look like this:

protected System.Windows.FrameworkElement CreateSurveyorControlFor(object surveyDefinition)
{
    Surveyor surveyorControl = new Surveyor();
    
return surveyorControl;
}

ApplicationUnderTest drives all this:

namespace UnitTestingAvalon
{
    
using System;
    
using System.Threading;
    
using System.Windows;
    
using System.Windows.Automation;
    
using System.Windows.Threading;
    
internal static class ApplicationUnderTest
    
{
        
#region ApplicationThread
        
private static Thread ApplicationThread { get { return ApplicationUnderTest.applicationThread; } set { ApplicationUnderTest.applicationThread = value; } }
        
private static Thread applicationThread;
        
#endregion ApplicationThread

        
#region HostWindow
        
internal static Window HostWindow { get { return ApplicationUnderTest.hostWindow; } set { ApplicationUnderTest.hostWindow = value; } }
        
private static Window hostWindow = null;
        
#endregion HostWindow

        #region Initialize / Deinitialize
        
internal static void Initialize(WindowRunner.CreateContent contentCreator, object contentInformation)
        
{
            
ApplicationUnderTest.CreateHostWindow(contentCreator, contentInformation);
        
}

        internal static void Deinitialize()
        
{
            
if (ApplicationUnderTest.HostWindow != null)
            
{
                
ApplicationUnderTest.Invoke(delegate { ApplicationUnderTest.HostWindow.Close(); });
                
ApplicationUnderTest.HostWindow = null;
            
}

            
ApplicationUnderTest.ApplicationThread.Join();
            
ApplicationUnderTest.ApplicationThread = null;
        
}
        
#endregion Initialize

        
#region Avalon Invoke
        
// Avalon requires all interaction with UI to happen on the UI's thread. This invoke simplifies doing so.
        
internal delegate void DispatcherInvokeCallback();
        
internal static void Invoke(DispatcherInvokeCallback callback)
        
{
            
ApplicationUnderTest.HostWindow.Dispatcher.Invoke(DispatcherPriority.Normal, callback);
        
}
        
#endregion Avalon Invoke

        
#region WaitForIdle
        
private static void WaitForIdle(int timeout)
        {
            
if (timeout <= 0)
            
{
                
throw new ArgumentException("The timeout specified must be greater than zero.");
            
}

            
DispatcherOperationCallback operationCallback = new DispatcherOperationCallback(delegate { return null; });
            
ApplicationUnderTest.HostWindow.Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new TimeSpan(0, 0, timeout), operationCallback, null);
        
}
        
#endregion WaitForIdle

        
#region CreateHostWindow
        
private static void CreateHostWindow(WindowRunner.CreateContent contentCreator, object contentInformation)
        
{
            WindowRunner windowRunner = new WindowRunner(ApplicationUnderTest.RetrieveWindow, contentCreator, contentInformation);
            
ApplicationUnderTest.ApplicationThread = new System.Threading.Thread(new System.Threading.ThreadStart(windowRunner.Run));
            
// Avalon requires UI to run on STA threads.
            
ApplicationUnderTest.ApplicationThread.SetApartmentState(System.Threading.ApartmentState.STA);
            
ApplicationUnderTest.ApplicationThread.Start();
            
// Give up the rest of our timeslice so the application's thread gets scheduled. This is required on uniprocessor machines.
            
Thread.Sleep(0);
        
}
        #endregion CreateHostWindow

        
#region RetrieveWindow
        
private static void RetrieveWindow(System.Windows.Window window)
        
{
            
ApplicationUnderTest.HostWindow = window;
        
}
        
#endregion RetrieveWindow
    
}
}

So here's the flow:

  1. Your Test Method Setup calls ApplicationUnderTest.Initialize, providing the method that will create your UI as well as any random information that method requires.
  2. ApplicationUnderTest.Initialize calls ApplicationUnderTest.CreateHostWindow.
  3. ApplicationUnderTest.CreateHostWindow creates a new WindowRunner instance and starts it running on a separate thread.
  4. WindowRunner.Run creates a new Avalon.Window.
  5. WindowRunner.Run stuffs the return value from your UI creation method into the Window.
  6. WindowRunner.Run calls back into ApplicationUnderTest to give it a reference to the Window.
  7. WindowRunner.Run shows the Window modally.
  8. ApplicationUnderTest.CreateHostWindow blocks until the Window appears (not shown here).
  9. ApplicationUnderTest.CreateHostWindow and then .Initialize return.
  10. Your test case executes.
  11. Your Test Method Teardown calls ApplicationUnderTest.Deinitialize.
  12. ApplicationUnderTest.Deinitialize closes the Window.
  13. ApplicationUnderTest.Deinitialize blocks until the Window's thread exits.
  14. ApplicationUnderTest.Deinitialize returns.