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/articles/nunit/writing-tests/attributes/retry.md b/docs/articles/nunit/writing-tests/attributes/retry.md index a50f2c94a..33426d0fd 100644 --- a/docs/articles/nunit/writing-tests/attributes/retry.md +++ b/docs/articles/nunit/writing-tests/attributes/retry.md @@ -2,12 +2,17 @@ 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). +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. + + 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)] 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 +} 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