From e913cf14ffcf1056d2dba2d2c02f1bac1af752bd Mon Sep 17 00:00:00 2001 From: Manfred Brands Date: Mon, 24 Nov 2025 14:42:33 +0800 Subject: [PATCH 1/3] Add Retry and Retry with RetryExceptions examples --- .../nunit/writing-tests/attributes/retry.md | 10 ++- .../Attributes/RetryAttributeExamples.cs | 69 +++++++++++++++++++ 2 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 docs/snippets/Snippets.NUnit/Attributes/RetryAttributeExamples.cs diff --git a/docs/articles/nunit/writing-tests/attributes/retry.md b/docs/articles/nunit/writing-tests/attributes/retry.md index a50f2c94a..2aa1f019e 100644 --- a/docs/articles/nunit/writing-tests/attributes/retry.md +++ b/docs/articles/nunit/writing-tests/attributes/retry.md @@ -2,12 +2,16 @@ RetryAttribute is used on a test method to specify that it should be rerun if it fails, up to a maximum number of times. +[!code-csharp[Retry](~/snippets/Snippets.NUnit/Attributes/RetryAttributeExamples.cs#Retry)] + Notes: 1. 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. 2. It is not currently possible to use `RetryAttribute` on a `TestFixture` or any other type of test suite. Only single tests may be repeated. -3. If a test has an unexpected exception, an error result is returned and it is not retried. Only assertion failures can - trigger a retry. To convert an unexpected exception into an assertion failure, see the - [ThrowsConstraint](xref:throwsconstraint). +3. If a test has an unexpected exception, an error result is returned and it is not retried. + You can enable retry on an expected exception such as `TimeoutException` by setting the `RetryExceptions` property. + The value of this property is an array of anticipated exceptions that should be retried. + +[!code-csharp[RetryWithRetryExceptions](~/snippets/Snippets.NUnit/Attributes/RetryAttributeExamples.cs#RetryWithRetryExceptions)] diff --git a/docs/snippets/Snippets.NUnit/Attributes/RetryAttributeExamples.cs b/docs/snippets/Snippets.NUnit/Attributes/RetryAttributeExamples.cs new file mode 100644 index 000000000..54fb2510f --- /dev/null +++ b/docs/snippets/Snippets.NUnit/Attributes/RetryAttributeExamples.cs @@ -0,0 +1,69 @@ +using System.Diagnostics; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Snippets.NUnit; + +public class RetryAttributeExamples +{ + #region Retry + [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 ExpensiveOperation() + { + // Simulate an expensive operation + int duration = _random.Next(500, 1500); + await Task.Delay(duration); // Simulate work + return "Actual Result"; // Simulate a response + } + } + #endregion + + #region RetryWithRetryExceptions + [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 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 + } + } + #endregion +} From a785b49fc0dd4c9ef8cda406baaa52e31534d86a Mon Sep 17 00:00:00 2001 From: Manfred Brands Date: Mon, 24 Nov 2025 14:56:54 +0800 Subject: [PATCH 2/3] Update to latest nunit alpha package --- .github/workflows/build-process.yml | 2 +- docs/snippets/Snippets.NUnit/Snippets.NUnit.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-process.yml b/.github/workflows/build-process.yml index 96fa8fe6f..95128a6f2 100644 --- a/.github/workflows/build-process.yml +++ b/.github/workflows/build-process.yml @@ -79,7 +79,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v5 with: - dotnet-version: 9.x + dotnet-version: 10.x - name: Restore dependencies run: dotnet restore $SOLUTION_LOCATION - name: Build diff --git a/docs/snippets/Snippets.NUnit/Snippets.NUnit.csproj b/docs/snippets/Snippets.NUnit/Snippets.NUnit.csproj index b2cad4ce0..b7d649022 100644 --- a/docs/snippets/Snippets.NUnit/Snippets.NUnit.csproj +++ b/docs/snippets/Snippets.NUnit/Snippets.NUnit.csproj @@ -10,7 +10,7 @@ - + all From 077bf03f8de7bc9290af3b875ca66a787dda6f00 Mon Sep 17 00:00:00 2001 From: Manfred Brands Date: Mon, 24 Nov 2025 15:10:18 +0800 Subject: [PATCH 3/3] Add a note that RetryExceptions is only available from NUnit 4.5.0 --- docs/articles/nunit/writing-tests/attributes/retry.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/articles/nunit/writing-tests/attributes/retry.md b/docs/articles/nunit/writing-tests/attributes/retry.md index 2aa1f019e..33426d0fd 100644 --- a/docs/articles/nunit/writing-tests/attributes/retry.md +++ b/docs/articles/nunit/writing-tests/attributes/retry.md @@ -8,10 +8,11 @@ Notes: 1. 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. -2. It is not currently possible to use `RetryAttribute` on a `TestFixture` or any other type of test suite. Only single - tests may be repeated. +2. It is not currently possible to use `RetryAttribute` on a `TestFixture` or any other type of test suite. + Only single tests may be repeated. 3. If a test has an unexpected exception, an error result is returned and it is not retried. - You can enable retry on an expected exception such as `TimeoutException` by setting the `RetryExceptions` property. - The value of this property is an array of anticipated exceptions that should be retried. + + From NUnit 4.5.0 you can enable retry on an expected exception such as `TimeoutException` + by setting the `RetryExceptions` property. The value of this property is an array of anticipated exceptions that should be retried. [!code-csharp[RetryWithRetryExceptions](~/snippets/Snippets.NUnit/Attributes/RetryAttributeExamples.cs#RetryWithRetryExceptions)]