From 29ef55bac3c70dbe46c8f25bb03810a2f7a186b7 Mon Sep 17 00:00:00 2001 From: martintmk <103487740+martintmk@users.noreply.github.com> Date: Fri, 22 Sep 2023 08:14:51 +0200 Subject: [PATCH] [Docs] Testing (#1608) --- docs/advanced/testing.md | 63 +++++++++++++++++++++++++++ docs/index.md | 1 + docs/toc.yml | 2 + src/Polly.RateLimiting/README.md | 56 ++++++++---------------- src/Polly.Testing/README.md | 13 ++++-- src/Snippets/Docs/Testing.cs | 61 ++++++++++++++++++++++++++ src/Snippets/RateLimiting/Snippets.cs | 52 ---------------------- src/Snippets/Snippets.csproj | 2 +- src/Snippets/Testing/Snippets.cs | 39 ----------------- 9 files changed, 156 insertions(+), 133 deletions(-) create mode 100644 docs/advanced/testing.md create mode 100644 src/Snippets/Docs/Testing.cs delete mode 100644 src/Snippets/RateLimiting/Snippets.cs delete mode 100644 src/Snippets/Testing/Snippets.cs diff --git a/docs/advanced/testing.md b/docs/advanced/testing.md new file mode 100644 index 00000000000..d7e3ebffa17 --- /dev/null +++ b/docs/advanced/testing.md @@ -0,0 +1,63 @@ +# Testing + +This document explains how to test Polly’s resilience pipelines. You should not test how the resilience pipelines operate internally, but rather test your own settings or custom delegates. + +To make the testing process simpler, Polly offers the [`Polly.Testing`](https://www.nuget.org/packages/Polly.Testing/) package. This package has a range of APIs designed to help you test the setup and combination of resilience pipelines in your user code. + +## Usage + +Begin by adding the `Polly.Testing` package to your test project: + +```sh +dotnet add package Polly.Testing +``` + +Use the `GetPipelineDescriptor` extension method to get the [`ResiliencePipelineDescriptor`](xref:Polly.Testing.ResiliencePipelineDescriptor) which provides details on the pipeline's composition: + + +```cs +// Build your resilience pipeline. +ResiliencePipeline pipeline = new ResiliencePipelineBuilder() + .AddRetry(new RetryStrategyOptions + { + MaxRetryAttempts = 4 + }) + .AddTimeout(TimeSpan.FromSeconds(1)) + .Build(); + +// Retrieve the descriptor. +ResiliencePipelineDescriptor descriptor = pipeline.GetPipelineDescriptor(); + +// Check the pipeline's composition with the descriptor. +Assert.Equal(2, descriptor.Strategies.Count); + +// Verify the retry settings. +var retryOptions = Assert.IsType(descriptor.Strategies[0].Options); +Assert.Equal(4, retryOptions.MaxRetryAttempts); + +// Confirm the timeout settings. +var timeoutOptions = Assert.IsType(descriptor.Strategies[1].Options); +Assert.Equal(TimeSpan.FromSeconds(1), timeoutOptions.Timeout); +``` + + +The `GetPipelineDescriptor` extension method is also available for the generic `ResiliencePipeline`: + + +```cs +// Construct your resilience pipeline. +ResiliencePipeline pipeline = new ResiliencePipelineBuilder() + .AddRetry(new RetryStrategyOptions + { + MaxRetryAttempts = 4 + }) + .AddTimeout(TimeSpan.FromSeconds(1)) + .Build(); + +// Obtain the descriptor. +ResiliencePipelineDescriptor descriptor = pipeline.GetPipelineDescriptor(); + +// Check the pipeline's composition with the descriptor. +// ... +``` + diff --git a/docs/index.md b/docs/index.md index 5ea87a81e3a..88787ba407b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -35,6 +35,7 @@ Polly has a rich documentation that covers various topics, such as: - [Telemetry and monitoring](advanced/telemetry.md): How to access and analyze the data generated by Polly strategies and pipelines. - [Dependency injection](advanced/dependency-injection.md): How to integrate Polly with dependency injection frameworks and containers. - [Performance](advanced/performance.md): Tips on optimizing and getting the best performance from Polly. +- [Testing](advanced/testing.md): How to test the composition and configuration of resilience pipelines. - [Chaos engineering](advanced/simmy.md): How to use Polly to inject faults and test the resilience of your system. - [Extensibility](extensibility/index.md): How to create and use custom strategies and extensions for Polly. diff --git a/docs/toc.yml b/docs/toc.yml index f4b182103e0..44dd40deda5 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -43,6 +43,8 @@ href: advanced/resilience-context.md - name: Performance href: advanced/performance.md + - name: Testing + href: advanced/testing.md - name: Chaos engineering href: advanced/simmy.md diff --git a/src/Polly.RateLimiting/README.md b/src/Polly.RateLimiting/README.md index ff346fb90a9..fa10f2c3667 100644 --- a/src/Polly.RateLimiting/README.md +++ b/src/Polly.RateLimiting/README.md @@ -6,46 +6,28 @@ The `Polly.RateLimiting` package adopts the [.NET Rate Limiting](https://devblog - It exposes the `AddConcurrencyLimiter` convenience extension methods for `ResiliencePipelineBuilder`. - It exposes the `RateLimiterRejectedException` class to notify the caller that the operation was rate limited. -Example: +See for more details. - -```cs -ResiliencePipelineBuilder builder = new ResiliencePipelineBuilder(); - -// Convenience extension method for ConcurrencyLimiter -builder.AddConcurrencyLimiter(permitLimit: 10, queueLimit: 10); - -// Convenience extension method for ConcurrencyLimiter with callback -builder.AddConcurrencyLimiter( - new ConcurrencyLimiterOptions - { - PermitLimit = 10, - QueueLimit = 10 - }); +## Usage -// Convenience extension method with custom limiter creation -builder.AddRateLimiter( - new ConcurrencyLimiter(new ConcurrencyLimiterOptions + +```cs +// Add rate limiter with default options. +// See https://www.pollydocs.org/strategies/rate-limiter#defaults for defaults. +new ResiliencePipelineBuilder() + .AddRateLimiter(new RateLimiterStrategyOptions()); + +// Create a rate limiter to allow a maximum of 100 concurrent executions and a queue of 50. +new ResiliencePipelineBuilder() + .AddConcurrencyLimiter(100, 50); + +// Create a rate limiter that allows 100 executions per minute. +new ResiliencePipelineBuilder() + .AddRateLimiter(new SlidingWindowRateLimiter(new SlidingWindowRateLimiterOptions { - PermitLimit = 10, - QueueLimit = 10 + PermitLimit = 100, + Window = TimeSpan.FromMinutes(1) })); - -// Add rate limiter using the RateLimiterStrategyOptions -var limiter = new ConcurrencyLimiter(new ConcurrencyLimiterOptions -{ - PermitLimit = 10, - QueueLimit = 10 -}); - -builder.AddRateLimiter(new RateLimiterStrategyOptions -{ - RateLimiter = args => limiter.AcquireAsync(1, args.Context.CancellationToken), - OnRejected = _ => - { - Console.WriteLine("Rate limiter rejected!"); - return default; - } -}); ``` + diff --git a/src/Polly.Testing/README.md b/src/Polly.Testing/README.md index c2720652ce0..634b842c937 100644 --- a/src/Polly.Testing/README.md +++ b/src/Polly.Testing/README.md @@ -2,6 +2,10 @@ This package exposes APIs and utilities that can be used to assert on the composition of resilience pipelines. +See for more details. + +## Usage + ```cs // Build your resilience pipeline. @@ -11,19 +15,20 @@ ResiliencePipeline pipeline = new ResiliencePipelineBuilder() MaxRetryAttempts = 4 }) .AddTimeout(TimeSpan.FromSeconds(1)) - .ConfigureTelemetry(NullLoggerFactory.Instance) .Build(); -// Retrieve inner strategies. +// Retrieve the descriptor. ResiliencePipelineDescriptor descriptor = pipeline.GetPipelineDescriptor(); -// Assert the composition. +// Check the pipeline's composition with the descriptor. Assert.Equal(2, descriptor.Strategies.Count); +// Verify the retry settings. var retryOptions = Assert.IsType(descriptor.Strategies[0].Options); Assert.Equal(4, retryOptions.MaxRetryAttempts); -var timeoutOptions = Assert.IsType(descriptor.Strategies[0].Options); +// Confirm the timeout settings. +var timeoutOptions = Assert.IsType(descriptor.Strategies[1].Options); Assert.Equal(TimeSpan.FromSeconds(1), timeoutOptions.Timeout); ``` diff --git a/src/Snippets/Docs/Testing.cs b/src/Snippets/Docs/Testing.cs new file mode 100644 index 00000000000..88842e6d975 --- /dev/null +++ b/src/Snippets/Docs/Testing.cs @@ -0,0 +1,61 @@ +using Polly.Retry; +using Polly.Testing; +using Polly.Timeout; +using Xunit; + +namespace Snippets.Docs; + +internal static class Testing +{ + public static void GetPipelineDescriptor() + { + #region get-pipeline-descriptor + + // Build your resilience pipeline. + ResiliencePipeline pipeline = new ResiliencePipelineBuilder() + .AddRetry(new RetryStrategyOptions + { + MaxRetryAttempts = 4 + }) + .AddTimeout(TimeSpan.FromSeconds(1)) + .Build(); + + // Retrieve the descriptor. + ResiliencePipelineDescriptor descriptor = pipeline.GetPipelineDescriptor(); + + // Check the pipeline's composition with the descriptor. + Assert.Equal(2, descriptor.Strategies.Count); + + // Verify the retry settings. + var retryOptions = Assert.IsType(descriptor.Strategies[0].Options); + Assert.Equal(4, retryOptions.MaxRetryAttempts); + + // Confirm the timeout settings. + var timeoutOptions = Assert.IsType(descriptor.Strategies[1].Options); + Assert.Equal(TimeSpan.FromSeconds(1), timeoutOptions.Timeout); + + #endregion + } + + public static void GetPipelineDescriptorGeneric() + { + #region get-pipeline-descriptor-generic + + // Construct your resilience pipeline. + ResiliencePipeline pipeline = new ResiliencePipelineBuilder() + .AddRetry(new RetryStrategyOptions + { + MaxRetryAttempts = 4 + }) + .AddTimeout(TimeSpan.FromSeconds(1)) + .Build(); + + // Obtain the descriptor. + ResiliencePipelineDescriptor descriptor = pipeline.GetPipelineDescriptor(); + + // Check the pipeline's composition with the descriptor. + // ... + + #endregion + } +} diff --git a/src/Snippets/RateLimiting/Snippets.cs b/src/Snippets/RateLimiting/Snippets.cs deleted file mode 100644 index 51720b84ecd..00000000000 --- a/src/Snippets/RateLimiting/Snippets.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Threading.RateLimiting; -using Polly.RateLimiting; - -namespace Snippets.RateLimiting; - -internal static class Snippets -{ - public static void Usage() - { - #region rate-limiter-usage - - ResiliencePipelineBuilder builder = new ResiliencePipelineBuilder(); - - // Convenience extension method for ConcurrencyLimiter - builder.AddConcurrencyLimiter(permitLimit: 10, queueLimit: 10); - - // Convenience extension method for ConcurrencyLimiter with callback - builder.AddConcurrencyLimiter( - new ConcurrencyLimiterOptions - { - PermitLimit = 10, - QueueLimit = 10 - }); - - // Convenience extension method with custom limiter creation - builder.AddRateLimiter( - new ConcurrencyLimiter(new ConcurrencyLimiterOptions - { - PermitLimit = 10, - QueueLimit = 10 - })); - - // Add rate limiter using the RateLimiterStrategyOptions - var limiter = new ConcurrencyLimiter(new ConcurrencyLimiterOptions - { - PermitLimit = 10, - QueueLimit = 10 - }); - - builder.AddRateLimiter(new RateLimiterStrategyOptions - { - RateLimiter = args => limiter.AcquireAsync(1, args.Context.CancellationToken), - OnRejected = _ => - { - Console.WriteLine("Rate limiter rejected!"); - return default; - } - }); - - #endregion - } -} diff --git a/src/Snippets/Snippets.csproj b/src/Snippets/Snippets.csproj index e1a386f3e01..9aca2ff1c21 100644 --- a/src/Snippets/Snippets.csproj +++ b/src/Snippets/Snippets.csproj @@ -23,7 +23,7 @@ - + diff --git a/src/Snippets/Testing/Snippets.cs b/src/Snippets/Testing/Snippets.cs deleted file mode 100644 index 39a973b3433..00000000000 --- a/src/Snippets/Testing/Snippets.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Microsoft.Extensions.Logging.Abstractions; -using Polly.Retry; -using Polly.Testing; -using Polly.Timeout; -using Xunit; - -namespace Snippets.Testing; - -internal static class Snippets -{ - public static void GetPipelineDescriptor() - { - #region get-pipeline-descriptor - - // Build your resilience pipeline. - ResiliencePipeline pipeline = new ResiliencePipelineBuilder() - .AddRetry(new RetryStrategyOptions - { - MaxRetryAttempts = 4 - }) - .AddTimeout(TimeSpan.FromSeconds(1)) - .ConfigureTelemetry(NullLoggerFactory.Instance) - .Build(); - - // Retrieve inner strategies. - ResiliencePipelineDescriptor descriptor = pipeline.GetPipelineDescriptor(); - - // Assert the composition. - Assert.Equal(2, descriptor.Strategies.Count); - - var retryOptions = Assert.IsType(descriptor.Strategies[0].Options); - Assert.Equal(4, retryOptions.MaxRetryAttempts); - - var timeoutOptions = Assert.IsType(descriptor.Strategies[0].Options); - Assert.Equal(TimeSpan.FromSeconds(1), timeoutOptions.Timeout); - - #endregion - } -}