bas
bas

Reputation: 14912

Start WPF app from unit test in its own AppDomain

I am trying to run a WPF App from nunit. Since I only can run one App per AppDomain I instantiate a new AppDomain per acceptance test. When I do that, I run into serialization exceptions.

namespace Tests
{
    [TestFixture, RequiresSTA, Serializable]
    public class ApplicationTests
    {
        private MainWindow mainWindow;
        private bool guiVisible;
        private App app;

        [TestCase("app domain name for instance of App")]
        [TestCase("app domain name for another instance of App")]
        public void ApplicationTest(string name)
        {
            AppDomain appDomain = AppDomain.CreateDomain(name);
            //appDomain.ExecuteAssembly(@"C:\Users\bp\Documents\Visual Studio 2013\Projects\WpfApplication1\WpfApplication1\bin\Debug\WpfApplication1.exe");

            CrossAppDomainDelegate action = () =>
            {
                app = new App();
                app.InitializeComponent();
                app.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => AppOnActivated(null, null)));
                app.Run();
            };
            appDomain.DoCallBack(action);
        }

        private void AppOnActivated(object sender, EventArgs eventArgs)
        {
            if (!guiVisible)
            {
                mainWindow = (MainWindow)Application.Current.MainWindow;
                mainWindow.ButtonViewModel = new ButtonViewModel();
                mainWindow.ButtonViewModel.Name = "bla";

                guiVisible = true;
            }

            app.Shutdown();
        }
    }
}

The exception I receive now:

System.Runtime.Serialization.SerializationException : Type is not resolved for member 'Tests.ApplicationTests,Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

I made the test class [Serializable] which does not help either.

Help is very much appreciated. I just want to start my WPF application from an NUnit test so that I can write acceptance tests for my application. I keep running into different walls and eventually whatever path I choose seems to lead to a dead end...

Many thanks in advance,

Bas

Upvotes: 2

Views: 2810

Answers (1)

Eli Arbel
Eli Arbel

Reputation: 22739

AppDomains are software-isolated processes in .NET. This means you can't just reference objects belonging to one AppDomain from another. Objects can be either copied by value (serialization) or by reference using MarshalByRefObject. Since WPF's objects are neither of those, you can't move them around AppDomains.

For your your testing purposes, you could use a simpler approach: run everything within the new AppDomain, and use the SetData and GetData methods to transfer data to assert on.

[TestCase("app domain name for instance of App")]
[TestCase("app domain name for another instance of App")]
public void ApplicationTest(string name)
{
    AppDomain appDomain = AppDomain.CreateDomain(name, 
        AppDomain.CurrentDomain.Evidence,
        AppDomain.CurrentDomain.SetupInformation);
    appDomain.DoCallBack(StartApp);
    Assert.IsTrue((bool)appDomain.GetData("GuiVisible"));
    AppDomain.Unload(appDomain);
}

// using a static method instead of a lambda makes sure
// you haven't captured anything
private static void StartApp()
{
    app = new App();
    app.InitializeComponent();
    app.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
       new Action(() => AppOnActivated()));
    app.Run();
}

private static void AppOnActivated()
{
    var mainWindow = (MainWindow)Application.Current.MainWindow;
    mainWindow.ButtonViewModel = new ButtonViewModel();
    mainWindow.ButtonViewModel.Name = "bla";

    AppDomain.CurrentDomain.SetValue("GuiVisible") = true;

    app.Shutdown();
}

Upvotes: 4

Related Questions