Skip to content

Commit e913cf1

Browse files
Add Retry and Retry with RetryExceptions examples
1 parent 07bbbab commit e913cf1

File tree

2 files changed

+76
-3
lines changed

2 files changed

+76
-3
lines changed

docs/articles/nunit/writing-tests/attributes/retry.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
RetryAttribute is used on a test method to specify that it should be rerun if it fails, up to a maximum number of times.
44

5+
[!code-csharp[Retry](~/snippets/Snippets.NUnit/Attributes/RetryAttributeExamples.cs#Retry)]
6+
57
Notes:
68

79
1. The argument you specify is the total number of attempts and __not__ the number of retries after an initial failure.
810
So `[Retry(1)]` does nothing and should not be used.
911
2. It is not currently possible to use `RetryAttribute` on a `TestFixture` or any other type of test suite. Only single
1012
tests may be repeated.
11-
3. If a test has an unexpected exception, an error result is returned and it is not retried. Only assertion failures can
12-
trigger a retry. To convert an unexpected exception into an assertion failure, see the
13-
[ThrowsConstraint](xref:throwsconstraint).
13+
3. If a test has an unexpected exception, an error result is returned and it is not retried.
14+
You can enable retry on an expected exception such as `TimeoutException` by setting the `RetryExceptions` property.
15+
The value of this property is an array of anticipated exceptions that should be retried.
16+
17+
[!code-csharp[RetryWithRetryExceptions](~/snippets/Snippets.NUnit/Attributes/RetryAttributeExamples.cs#RetryWithRetryExceptions)]
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System.Diagnostics;
2+
using System.Threading.Tasks;
3+
using NUnit.Framework;
4+
5+
namespace Snippets.NUnit;
6+
7+
public class RetryAttributeExamples
8+
{
9+
#region Retry
10+
[TestFixture]
11+
public sealed class RetryTests
12+
{
13+
private readonly Random _random = new(42);
14+
15+
[Test]
16+
[Retry(5)]
17+
public async Task OperationShouldPassIn1s()
18+
{
19+
var sw = Stopwatch.StartNew();
20+
string result = await ExpensiveOperation();
21+
sw.Stop();
22+
Assert.That(sw.ElapsedMilliseconds, Is.LessThan(1000), "Operation did not complete in time");
23+
Assert.That(result, Is.Not.Null);
24+
}
25+
26+
private async Task<string> ExpensiveOperation()
27+
{
28+
// Simulate an expensive operation
29+
int duration = _random.Next(500, 1500);
30+
await Task.Delay(duration); // Simulate work
31+
return "Actual Result"; // Simulate a response
32+
}
33+
}
34+
#endregion
35+
36+
#region RetryWithRetryExceptions
37+
[TestFixture]
38+
public sealed class Retry
39+
{
40+
private int _delayInMilliseconds;
41+
42+
[OneTimeSetUp]
43+
public void Setup()
44+
{
45+
_delayInMilliseconds = 2500;
46+
}
47+
48+
[Test]
49+
[Retry(5, RetryExceptions = [typeof(OperationCanceledException)])]
50+
[CancelAfter(2000)]
51+
public async Task QueryServiceAsync(CancellationToken cancellationToken)
52+
{
53+
string result = await CallExternalServiceAsync(cancellationToken);
54+
Assert.That(result, Is.Not.Null);
55+
}
56+
57+
private async Task<string> CallExternalServiceAsync(CancellationToken cancellationToken)
58+
{
59+
// Call an external service that may time out
60+
int delayInMilliseconds = _delayInMilliseconds;
61+
if (_delayInMilliseconds > 1000)
62+
_delayInMilliseconds -= 1000; // Decrease delay for next attempt
63+
64+
await Task.Delay(delayInMilliseconds, cancellationToken); // Simulate a delay that may exceed
65+
return "Actual Result"; // Simulate a response
66+
}
67+
}
68+
#endregion
69+
}

0 commit comments

Comments
 (0)