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");
}
KrisTrip
  • 4,943
  • 12
  • 55
  • 74
  • 1
    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? – John Saunders Sep 27 '12 at 16:33
  • 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). – KrisTrip Sep 27 '12 at 16:35
  • 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... – Peter Ritchie Sep 27 '12 at 20:20
  • 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). – KrisTrip Sep 28 '12 at 15:19

3 Answers3

13

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 ...
}
KrisTrip
  • 4,943
  • 12
  • 55
  • 74
  • My objects under test were simply accessing Application.Current.Properties so this worked for me too. – dudeNumber4 Nov 20 '15 at 14:53
  • 1
    When trying to use your solution I get the following error "The calling thread cannot access this object because a different thread owns it." – Eido95 Aug 11 '16 at 19:52
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
    }
}
-1

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

Panos Rontogiannis
  • 4,154
  • 1
  • 24
  • 29