NUnit.ApplicationDomain
11.1.0
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
<PackageReference Include="NUnit.ApplicationDomain" Version="11.1.0" />
paket add NUnit.ApplicationDomain --version 11.1.0
#r "nuget: NUnit.ApplicationDomain, 11.1.0"
// 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 | Versions 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. |
-
.NETFramework 4.0
- NUnit (>= 3.7.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