Application.Current is Null for Unit Tests
Asked Answered
O

3

9

I have some methods in the code base that rely on Application.Current.Dispatcher.Invoke... to make sure things run on the GUI thread. I am currently trying to write unit tests for these methods but (as expected) Application.Current is null so I'm getting a NullReferenceException.

I tried to run the affected tests in their own AppDomain as suggested here: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/786d5c06-0511-41c0-a6a2-5c4e44f8ffb6/

But Application.Current is still null when I do that. Shouldn't starting up an AppDomain set the Application.Current for me? Why is it still null?

My code: The base class:

[TestClass()]
[Serializable]
public class UnitTest
{
    protected void ExecuteInSeparateAppDomain(string methodName)
    {
        AppDomainSetup appDomainSetup = new AppDomainSetup();
        appDomainSetup.ApplicationBase = Environment.CurrentDirectory;
        AppDomain appDomain = AppDomain.CreateDomain(methodName, null, appDomainSetup);

        try
        {
            appDomain.UnhandledException += delegate(object sender, UnhandledExceptionEventArgs e)
            {
                throw e.ExceptionObject as Exception;
            };

            UnitTest unitTest = appDomain.CreateInstanceAndUnwrap(GetType().Assembly.GetName().Name, GetType().FullName) as UnitTest;

            MethodInfo methodInfo = unitTest.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

            if (methodInfo == null)
            {
                throw new InvalidOperationException(string.Format("Method '{0}' not found on type '{1}'.", methodName, unitTest.GetType().FullName));
            }

            try
            {
                methodInfo.Invoke(unitTest, null);
            }
            catch (System.Reflection.TargetInvocationException e)
            {
                throw e.InnerException;
            }
        }
        finally
        {
            AppDomain.Unload(appDomain);
        }
    }
}

The calling unit test (contained in a class that inherits from UnitTest):

[TestMethod()]
public void QualifierViewModel_FlagsAndLoadDatasets()
{
    ExecuteInSeparateAppDomain("TestLoadDataSets");
}
Olomouc answered 27/9, 2012 at 16:21 Comment(4)
Why are you trying to unit test GUI code? It's not strictly a unit test if it depends on the GUI. Can your code factor out its use of Application.Current?Hypallage
It's a view model but apparently a little too tightly integrated. I guess it is really more of an integration test than a unit test. No I can't really factor out the use of Application.Current at the moment (I'm just supposed to unit test, not touch the code).Olomouc
The Application.Current object will only be there if the GUI has been initialized, e.g. Dispatcher.Run has been called. This isn't normally done (or recommended) in unit tests...Fearsome
I can fake it by calling new Application() (see my workaround below). But I really don't like having to put all those tests into a single method. What I was really trying to do was make it so that it would be available for tests in separate methods (and threads).Olomouc
O
14

So for now I have a nasty workaround. I basically moved all the tests that are testing methods that use Application.Current to a single test (so they will all be on the same thread) and call new Application().

The nasty code:

[TestClass]
    public class IntegrationTests
    {
        private CedarFile testCedarFile;

        /// <summary>
        /// This test tests methods that utilize Application.Current. 
        /// They all have to be in the same test because they must run on the same thread to use the Application instance.
        /// </summary>
        [TestMethod]
        public void IntegrationTests_All()
        {
            new Application();

            QualifierViewModel_FlagsAndLoadDatasets();
            CurrentFilesViewModel_AddCedarFile();
            CurrentFilesViewModel_AddCedarFileReadOnly();
        }
... the private methods to test ...
}
Olomouc answered 27/9, 2012 at 19:53 Comment(2)
My objects under test were simply accessing Application.Current.Properties so this worked for me too.Pigeontoed
When trying to use your solution I get the following error "The calling thread cannot access this object because a different thread owns it."Creek
K
0

This workaround works for me:

[TestClass]
public class MockAppTests
{
    private static Application application = new Application() { ShutdownMode= ShutdownMode.OnExplicitShutdown };
}

[TestClass]
public class IntegrationTests : MockAppTests
{        
    [TestMethod]
    public void MyTest()
    {
        //test
    }
}
Kolnos answered 11/7, 2014 at 11:5 Comment(0)
A
-1

Try to remove the Serializable attribute and instead derive the UnitTest class from MarshalByRefObject.

Arborization answered 27/9, 2012 at 16:42 Comment(1)
Nope, Application.Current is still null so I still get a NullReferenceExceptionOlomouc

© 2022 - 2024 — McMap. All rights reserved.