CancelAfter
CancelAfterAttribute sets a timeout after which NUnit marks a CancellationToken as canceled. The test must observe that token (directly or indirectly) for cancellation to take effect.
Constructor
CancelAfterAttribute(int timeout)
| Parameter | Type | Description |
|---|---|---|
timeout |
int |
Timeout in milliseconds. When exceeded, the cancellation token supplied to the test is canceled (cooperative cancellation). |
Applies To
| Test Methods | Test Fixtures (Classes) | Assembly |
|---|---|---|
| ✅ | ✅ | ❌ |
Note
Applying CancelAfter to a fixture sets the default timeout for tests in that fixture unless overridden at method level.
Background
For .NET Core and later,
Thread.Abort as used by the
TimeoutAttribute can no longer be used, and there is therefore no way to interrupt an endless loop.
For all tests, one could use the --blame-hang(-timeout) options of dotnet test. However, this will stop any further
execution of the remaining tests.
To still be able to cancel tests, one has to move to cooperative cancellation. See Cancellation in Managed
Threads using a
CancellationToken.
The CancelAfterAttribute is used to specify a timeout value in milliseconds for a test case. If the test case runs
longer than the time specified, the supplied CancellationToken is set to canceled. It is however up to the test code
to check this token, either directly or indirectly.
The specified timeout value covers the test setup and teardown as well as the test method itself. Before and after
actions may also be included, depending on where they were specified. Since the timeout may occur during any of these
execution phases, no guarantees can be made as to what will be run and any of these phases of execution may be
incomplete. If only used on a test, once a test has timed out, its teardown methods are executed. NUnit will ensure
that the cancellation token will be available across all these phases. This also means that the token will be in the
"cancelled" state in one phase (ex: TearDown) if it were marked as cancelled during a previous phase. It therefore
should be used with caution in TearDown phases when getting passed down to other methods which perform cleanup as
an already-cancelled token may prevent HTTP or DB calls from being performed.
The attribute may also be specified on a fixture, in which case it indicates the default timeout for any subordinate test cases. When using the console runner, it is also possible to specify a default timeout on the command-line.
When used on test methods, NUnit automatically adds an extra argument to your method containing the NUnit
CancellationToken. If you want to check for cancellation in SetUp methods, you can use
TestContext.CurrentContext.CancellationToken
Examples
The CancelAfterAttribute supports cooperative cancellation in several test styles.
Simple test
[Test, CancelAfter(2_000)]
public void RunningTestUntilCanceled(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
/* */
}
}
Parameterized test with TestCase
[CancelAfter(2000)]
[TestCase("http://example.com/1")]
[TestCase("http://example.com/2")]
public async Task PotentiallyLongRunningTest(string uri, CancellationToken token)
{
await Task.Delay(10, token);
Assert.That(uri, Does.StartWith("http://"));
}
Parameterized test with TestCaseSource
private static int[] _simpleValues = [2, 4, 6, 8];
[TestCaseSource(nameof(_simpleValues)), CancelAfter(1_000)]
public void TestCaseSourceWithCancellationToken(int a, CancellationToken cancellationToken)
{
Assert.That(cancellationToken, Is.Not.Default);
while (!cancellationToken.IsCancellationRequested)
{
/* */
}
}
Notes
- When a debugger is attached, the timeout is not enforced.
- Use
TestContext.CurrentContext.CancellationTokeninSetUp/TearDownwhen you need the same token outside the test method.