Retry
RetryAttribute is used on a test method to specify that it should be rerun if it fails, up to a maximum number of times.
[TestFixture]
public sealed class RetryTests
{
private readonly Random _random = new(42);
[Test]
[Retry(5)]
public async Task OperationShouldPassIn1s()
{
var sw = Stopwatch.StartNew();
string result = await ExpensiveOperation();
sw.Stop();
Assert.That(sw.ElapsedMilliseconds, Is.LessThan(1000), "Operation did not complete in time");
Assert.That(result, Is.Not.Null);
}
private async Task<string> ExpensiveOperation()
{
// Simulate an expensive operation
int duration = _random.Next(500, 1500);
await Task.Delay(duration); // Simulate work
return "Actual Result"; // Simulate a response
}
}
Notes:
The argument you specify is the total number of attempts and not the number of retries after an initial failure. So
[Retry(1)]does nothing and should not be used.It is not currently possible to use
RetryAttributeon aTestFixtureor any other type of test suite. Only single tests may be repeated.If a test has an unexpected exception, an error result is returned and it is not retried.
From NUnit 4.5.0 you can enable retry on an expected exception such as
TimeoutExceptionby setting theRetryExceptionsproperty. The value of this property is an array of anticipated exceptions that should be retried.
[TestFixture]
public sealed class Retry
{
private int _delayInMilliseconds;
[OneTimeSetUp]
public void Setup()
{
_delayInMilliseconds = 2500;
}
[Test]
[Retry(5, RetryExceptions = [typeof(OperationCanceledException)])]
[CancelAfter(2000)]
public async Task QueryServiceAsync(CancellationToken cancellationToken)
{
string result = await CallExternalServiceAsync(cancellationToken);
Assert.That(result, Is.Not.Null);
}
private async Task<string> CallExternalServiceAsync(CancellationToken cancellationToken)
{
// Call an external service that may time out
int delayInMilliseconds = _delayInMilliseconds;
if (_delayInMilliseconds > 1000)
_delayInMilliseconds -= 1000; // Decrease delay for next attempt
await Task.Delay(delayInMilliseconds, cancellationToken); // Simulate a delay that may exceed
return "Actual Result"; // Simulate a response
}
}