NUnit 5 - New features
This page lists the enhancements in NUnit 5. For each one, it explains why it was needed and what it gives you. For changes that may require you to update existing code, see NUnit 5 - A summary of the breaking changes. For the full list of changes, see the release notes.
Test ordering and selection
Test dependencies with [DependsOnTest] and [DependsOnFixture]
- Why: Some tests have to run after others, for example database installation before upgrade tests. Until now,
the only tool was
[Order], which sets a relative position but doesn't say which test depends on which, and doesn't react when the earlier test fails. - What's new:
[DependsOnTest("TestName")]makes a test run after another test in the same fixture, and[DependsOnFixture(typeof(OtherFixture))]makes a fixture run after another fixture. If the dependency fails, the dependent test isn't run, unless you setAllowFailure = true. NUnit reports circular dependencies as errors. If you run a single test from your IDE, NUnit also runs the tests it depends on, even though the filter excludes them.
[Test]
public void Install() { ... }
[Test, DependsOnTest(nameof(Install))]
public void Upgrade() { ... }
Note
[Order] is now marked [Obsolete]. Use [DependsOnTest] or [DependsOnFixture] instead. A test can't be part of
both an Order chain and a dependency chain, and tests in a dependency chain can't run in parallel.
For more information, see DependsOnTest and DependsOnFixture.
TestContext.ActiveTests
Issue #5354
- Why: A
[SetUpFixture]often does expensive setup, such as starting a database or a browser, that only some tests need. There was no way to find out which tests would actually run after filtering, so the setup had to run every time. - What's new: In a
[OneTimeSetUp]or[OneTimeTearDown]method,TestContext.CurrentContext.ActiveTestslists the tests under the current suite that passed the filter. You can inspect them, for example their categories or attributes, and skip setup that no active test needs. The property works with every runner, and isnulloutside those methods.
For more information, see TestContext.
Assertions and test data
Type-safe TestCaseData expected results
Issue #4717
- Why: The generic
TestCaseData<T1, ...>classes, added in NUnit 4.1, typed the arguments. However,.Returns()still took anobject, so a wrongly typed expected result was only found at runtime. The fluent methods, such as.SetName(), also returned a plainTestCaseData, which lost the generic type. - What's new:
.Returns()on a genericTestCaseDatareturns aTestCaseDataWithReturn<T1, ..., TReturn>, so the compiler checks the expected result.TestCaseData.Create(...)infers the argument types. Fluent methods keep the generic type, and a generic instance can still be returned from a source declared asIEnumerable<TestCaseData>.
private static IEnumerable<TestCaseDataWithReturn<int, int, int>> AddCases()
{
yield return TestCaseData.Create(1, 2).Returns(3);
yield return TestCaseData.Create(2, 3).SetName("Two plus three").Returns(5);
}
For more information, see TestCaseData.
ParamName on argument exception constraints
Issue #3947
- Why: Checking which parameter caused an
ArgumentExceptionneeded the long form.With.Property("ParamName"), which uses a string and isn't checked by the compiler. - What's new: Generic exception constraints for
ArgumentExceptionand derived types have aParamNameproperty. It must come directly after the exception type in the constraint expression. It is a C# 14 extension property, so your test project needs C# 14 or later to use it.
Assert.That(() => Parse(null!), Throws.ArgumentNullException.ParamName.EqualTo("input"));
Assert.That(() => Parse(""), Throws.InstanceOf<ArgumentException>().ParamName.EqualTo("input"));
For more information, see Throws Constraint.
Compile-time checks and awaitable async assertions
Has.Attribute<T>()now only accepts attribute types, so a wrong type is a compiler error instead of a failure at runtime.Assert.ThrowsAsync,Assert.CatchAsyncandAssert.DoesNotThrowAsyncare now truly asynchronous and must be awaited.
Both are breaking changes, and are described in NUnit 5 - A summary of the breaking changes.
Timing and repetition
Warning threshold for [MaxTime]
Issue #5260
- Why:
[MaxTime]fails a test that takes too long, with nothing in between pass and fail. A test that is getting slower gave no signal until it crossed the limit. - What's new: The optional
WarningTimeproperty sets a lower threshold. A passing test that takes longer thanWarningTime, but less than the maximum, gets a Warning result. This makes performance drift visible before it becomes a failure.
[Test, MaxTime(2000, WarningTime = 1000)]
public void ImportLargeFile() { ... }
For more information, see MaxTime.
Pass-percentage threshold for [Repeat]
Issue #5220
- Why: Some systems are not deterministic, for example an LLM-based chat bot. For these, "passes 9 times out of
10" is a valid requirement, but
[Repeat]required every run to pass. - What's new:
RequiredPassPercentagesets the percentage of runs that must pass (1-100, default 100). When it is below 100, all runs are executed and the result reports how many passed. SetStopWhenOverallResultDetermined = trueto stop as soon as the outcome is certain, either because the threshold has already been reached or because it can no longer be reached.
[Test, Repeat(20, RequiredPassPercentage = 90, StopWhenOverallResultDetermined = true)]
public void ChatBotAnswersCorrectly() { ... }
For more information, see Repeat.
Platform support
.NET 10 target
Issue #5316
- Why: Projects targeting .NET 10 were using the .NET 8 build of NUnit, so they couldn't benefit from newer runtime APIs.
- What's new: NUnit now ships
net462,net8.0andnet10.0builds, and projects on .NET 10 or later get thenet10.0build.
Under the hood
These changes don't affect how you write tests, but they make NUnit faster and easier to maintain.
- Faster locking on .NET 10 (#5387): The work-item and parallel
dispatcher code now uses the
System.Threading.Locktype introduced in .NET 9, with a polyfill for older runtimes. Test projects on .NET 10 or later get the faster locking. - Argument validation throw helpers (#5348,
#5349): Manual argument checks were replaced with the built-in
ArgumentNullException.ThrowIfNullandArgumentOutOfRangeException.ThrowIf*helpers, polyfilled for .NET Framework. The CA1510 and CA1512 analyzer rules are now enforced in the build. - Simpler overload resolution (#5267): The
OverloadResolutionPriorityattributes added in 4.6 to handle the switch fromTestDelegatetoActionare removed. They are no longer needed now that the old delegate overloads are gone.