NUnit.ApplicationDomain 11.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package NUnit.ApplicationDomain --version 11.1.0                
NuGet\Install-Package NUnit.ApplicationDomain -Version 11.1.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NUnit.ApplicationDomain" Version="11.1.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add NUnit.ApplicationDomain --version 11.1.0                
#r "nuget: NUnit.ApplicationDomain, 11.1.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install NUnit.ApplicationDomain as a Cake Addin
#addin nuget:?package=NUnit.ApplicationDomain&version=11.1.0

// Install NUnit.ApplicationDomain as a Cake Tool
#tool nuget:?package=NUnit.ApplicationDomain&version=11.1.0                

Introduction

Sometimes when creating a library, you need to have a static initializer, property, or field. Unfortunately, testing static things isn't easy - you can't easily control when the objects are created, and you can't tell them to "uncreate" themselves so you can test it again.

[NUnit.ApplicationDomain][nuget] attempts to solve this problem by running a test in a separate app domain, isolating the static initializer to only that domain. At the end of the test, the app domain is destroyed and the next test can get a new app domain for a clean environment.

How To

First, include the NUnit.ApplicationDomain nuget package. Then decorate your test method with the RunInApplicationDomainAttribute.

RunInApplicationDomainAttribute

The RunInApplicationDomain attribute runs that test method in its separate app domain. Just put it on your test method:

#!csharp
[Test, RunInApplicationDomain]
public void MyTest()

AppDomainRunner.IsInTestAppDomain

Use the AppDomainRunner.IsInTestAppDomain method to detect if code with side-effects should be executed. This is especially useful in the setup and teardown methods, as those methods are invoked both in the "normal" app domain and the "test" app domain:

#!csharp
[SetUp]
public void Setup()
{
  // if we're not in the app domain, do not run the setup code
  if (!AppDomainRunner.IsInTestAppDomain)
    return;
    
  File.WriteAllText("fake.txt", "a file");
}

AppDomainRunner.DataStore

Use AppDomainRunner.DataStore as a way of passing data back and forth between the "normal" app domain and the "test" app domain:

#!csharp
internal class TestContextTests
{
  [SetUp]
  public void Setup()
  {
    // accessing TestContext.CurrentContext.TestDirectory from the app domain will throw an
    // exception but we need the test directory, so we pass it in via the data store. 
    if (!AppDomainRunner.IsInTestAppDomain)
    {
      AppDomainRunner.DataStore.Set("TestDirectory", TestContext.CurrentContext.TestDirectory);
    }
  }

  [Test, RunInApplicationDomain]
  public void VerifyItFails()
  {
    var testDirectory = AppDomainRunner.DataStore.Get<string>("TestDirectory");
    Console.WriteLine($"The test directory is: {testDirectory}");

    // we can also pass data back into the "normal" domain
    AppDomainRunner.DataStore.Set("ShouldBeSetFromAppDomain", testDirectory);
  }

  [TearDown]
  public void Teardown()
  {
    // okay, make sure everything worked properly now
    if (!AppDomainRunner.IsInTestAppDomain)
    {
      var testDirectory = AppDomainRunner.DataStore.Get<string>("ShouldBeSetFromAppDomain");

      // this should have been set by the test app-domain
      Assert.That(testDirectory, Is.EqualTo(TestContext.CurrentContext.TestDirectory));
    }
  }
}

This can be especially useful for verifying that something occurred in the app domain or for adding information that can't be accessed from the test app domain such as the members on TestContext.CurrentContext.

Gotchas

There are a couple things that you should know about the way the tests run:

  • The class containing the test method must be public
  • Only the test method, the setup method, and the test method will be called. Any extra NUnit parameters (such as ExpectedException, or RequiresSTA) will not be honored (if you want/needs support of this, create an issue).
  • The setup and teardown methods are invoked both normally and in the app domain. This results in the setup and teardown methods being called twice. It is advised to use AppDomainRunner.IsInTestAppDomain property to mitigate this problem.

Tests Returning Tasks

NUnit 3.0 introduced [better] support for async tests (tests that return a Task). The way NUnit.ApplicationDomain is implemented requires the library to somehow block until the test is completed before running the TearDown. It does this [by default] by invoking .Wait() on the returned task.

If you need to block via some other mechnism (for example using a Dispatcher, or pumping some sort of message thread) you can implement IAsyncTestResultHandler on your test class and then block using your own mechnism:

#!csharp
[Test]
public async Task WaitsForDispatcherToContinue()
{
  // pretend that for some made-up reason, we need to be in the event loop 
  await Dispatcher.Yield();

  // and pretend that something later triggers that allows us to complete
  await Task.Delay(TimeSpan.FromSeconds(3));

  AppDomainRunner.DataStore.Set<bool>("ran", true);
}

[TearDown]
public void Teardown()
{
  Assert.That(AppDomainRunner.DataStore.Get<bool>("ran"), Is.True);
}

/// <inheritdoc />
void IAsyncTestResultHandler.Process(Task task)
{
  // if we just simply did task.Wait(), we would block indefinitely because no-one is message
  // pumping. 

  // instead, tell the dispatcher to run until the task has resolved
  var frame = new DispatcherFrame();
  task.ContinueWith(_1 => frame.Continue = false);
  Dispatcher.PushFrame(frame);

  // propagate any exceptions
  task.Wait();
}

Full test/example is implemented as a test.

Product Compatible and additional computed target framework versions.
.NET Framework net40 is compatible.  net403 was computed.  net45 was computed.  net451 was computed.  net452 was computed.  net46 was computed.  net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.0

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
12.0.0 65,460 5/22/2021
11.1.0 53,908 7/18/2018
11.0.0 21,803 7/27/2017
10.2.0 7,492 12/4/2016
10.1.0-beta1 855 10/18/2016
10.0.0 4,141 10/15/2016
10.0.0-beta1 930 10/15/2016
9.0.0 1,754 10/4/2016
8.0.0 1,796 8/29/2016
7.0.0 1,639 4/2/2016
6.0.0 2,130 12/5/2015
5.0.1 19,844 4/29/2015
4.0.1 1,412 4/4/2015
3.0.1 2,023 11/24/2014
3.0.0 1,276 11/24/2014
2.0.1 1,543 10/11/2014
2.0.0 1,305 10/11/2014
1.0.3 1,451 8/20/2014
1.0.2 1,609 12/13/2013
1.0.1 1,462 6/22/2013

## Version 11.1.0
         - Fix: Add broader support for newer versions of NUnit (Brandon Ording)

         ## Version 11.0.0
         - Add support for (and a dependency on) NUnit 3.7.0

         ## Version 10.2.0
         - Add support for app-domain factories, allowing the constructed app-domain to
         be customized.

         ## Version 10.1.0
         - Add support for parameterized test fixtures (rubenhak)

         ## Version 10.0.0
         - Add waiting for Task-returning tests to complete
         - Add ability to customize behavior of task-returning tests

         ## Version 9.0.0
         - Fix: Unload test app-domains after use in order to lower resource usage (John Lewin)

         ## Version 8.0.0
         - Fix: Always run all teardown methods, even when the test method threw an exception
         - Add the ability to share data between the parent-domain and the test domain
         via AppDomainRunner.DataStore

         ## Version 7.0.0
         - Strong name the assembly
         - Add support for Mono (based on changes from Frederik Carlier)

         ## Version 6.0.0
         - Support NUnit 3.0, Drop NUnit 2 support

         ## Version 5.0.1
         - Architecture change to separate out the internal and public api
         - Allow app-domain test failures logging to be suppressed
         (AppDomainRunner.ShouldIncludeAppDomainErrorMessages)
         - Removed obsolete members
         - AppDomainTestRunnerBase removed
         - non-empty RunInApplicationDomainAttribute ctor removed

         ## Version 4.0.1
         - Add support for additional test runners/shadow copying

         ## Version 3.0.1
         - Add support for teardown methods
         - Add official support for multiple setup/teardown methods
         - Add AppDomainRunner.IsInTestAppDomain to detect if you're running in
         the test app domain

         ## Version 2.0.0
         - Find assembly location by escaping assembly codebases

         ## Version 1.0.3
         - Added support for abstract classes

         ## Version 1.0.1
         Initial Release