18

xUnit uses the same AppDomain for the whole test assembly, this is problematic as I'm testing a UI library and need to create a new Application instance for each individual test.

It works when I run a single test, but when I Run All the first test passed, but all subsequent tests fail with Cannot create more than one System.Windows.Application instance in the same AppDomain at the line where I create a new Application object.

4

3 Answers 3

6

Perhaps you could try by making your test class like this:

public class DomainIsolatedTests : IDisposable
{
  private static int index = 0;
  private readonly AppDomain testDomain;

  public DomainIsolatedTests()
  {
    var name= string.Concat("TestDomain #", ++index);
    testDomain = AppDomain.CreateDomain(name, AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
    // Trace.WriteLine(string.Format("[{0}] Created.", testDomain.FriendlyName)); 
  }

  public void Dispose()
  {
    if (testDomain != null)
    {        
      // Trace.WriteLine(string.Format("[{0}] Unloading.", testDomain.FriendlyName));
      AppDomain.Unload(testDomain);        
    }
  }

  [Fact]
  public void Test1()
  {
    testDomain.DoCallBack(() => {
      var app = new System.Windows.Application();

      ...
      // assert
    });
  }

  [Fact]
  public void Test2()
  {
    testDomain.DoCallBack(() => { 
      var app = new System.Windows.Application();

      ...
      // assert
    });
  }

  [Fact]
  public void Test3()
  {
    testDomain.DoCallBack(() => {
      var app = new System.Windows.Application();

      ...
      // assert
    });
  }

  ...
}
Sign up to request clarification or add additional context in comments.

4 Comments

Hm, this "works", but the problem is that even if a test fails in the DoCallBack method, xUnit reports that the test has succeeded. Seems like the exception is somehow swallowed
@Flagbug could you give an example of your failing test?
@Flugbug did you manage to propagate the exception so xUnit can detect it ?
The exceptions need to be Serializable. If not, I've found you still get an exception to "propogate" but it's really a new exception indicating that the original exception raised within the separate AppDomain is not flagged Serializable. Good enough for me as it still indicates the presence of an exception as far as I can tell.
0

Maybe, foreach Test you can dynamically load your assembly in a new Domain.

public static void DynamicExecution(String assemblyFileName, String uniqueDomName)
{
    Boolean retVal = false;

        AppDomain newDomain = AppDomain.CreateDomain(uniqueDomName);
        YourClass yourClass = (YourClass)newDomain.CreateInstanceFromAndUnwrap(assemblyFileName, "YourClass");

        //do what you need with yourClass Object


        AppDomain.Unload(newDomain);
        newDomain = null;
}

Comments

0
AppDomain appDomain = AppDomain.CreateDomain("WorkerDomain " + Thread.CurrentThread.Name);

var domain = (AppDomainWorker)appDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(AppDomainWorker).FullName);

domain.Executor();

internal class AppDomainWorker
{
    internal object Executor ()
    {
        // your unit test can run here
    }
}

Two more important things:

1) You might need to mark class as MarshalByRefObject and override InitializeLifetimeService method to have a object longer life or existence. MarshalByRefObject will be needed in case you have to communicate between appdomains. For details on this read up on Microsoft remoting concept.

2) Sometime we might need to assembly resolution in AppDomains, if it does not take the Assemblies loaded in the parent domain but it is in very rare cases.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.