NResilience.Testing 0.33.0

dotnet add package NResilience.Testing --version 0.33.0
                    
NuGet\Install-Package NResilience.Testing -Version 0.33.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NResilience.Testing" Version="0.33.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NResilience.Testing" Version="0.33.0" />
                    
Directory.Packages.props
<PackageReference Include="NResilience.Testing" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NResilience.Testing --version 0.33.0
                    
#r "nuget: NResilience.Testing, 0.33.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NResilience.Testing@0.33.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NResilience.Testing&version=0.33.0
                    
Install as a Cake Addin
#tool nuget:?package=NResilience.Testing&version=0.33.0
                    
Install as a Cake Tool

NResilience.Testing

Test helpers for NResilience: scripted callbacks, a recording telemetry listener, fake-time support, and a deterministic simulator, for fast tests that measure what a policy actually costs.

Install

Install the package using the .NET CLI:

dotnet add package NResilience.Testing

Why a separate package

Testing retries and timeouts against the real clock is slow and flaky - a 30-second timeout takes 30 seconds to test, and timing varies across machines. NResilience.Testing lets you script dependency behavior, capture policy events for assertion, and advance time manually, so a timeout test runs in microseconds.

It is a test-time dependency only and does not affect the core library in production.

Script the callback

Sequence<T> serves a script of returns, throws, and delays one by one as the policy makes attempts:

Sequence<HttpResponseMessage> calls = Sequence.For<HttpResponseMessage>()
    .Returns(new HttpResponseMessage(HttpStatusCode.ServiceUnavailable), count: 2)
    .Returns(new HttpResponseMessage(HttpStatusCode.OK));

var policy = Resilience.Http with { Backoff = Backoff.None };

CallResult<HttpResponseMessage> result = await policy.TryRunAsync(attempt => calls.NextAsync(attempt));

Assert.True(result.IsSuccess);
Assert.Equal(3, calls.CallCount);
Assert.Equal(3, result.Attempts.Count);

Control the clock

Pass the same FakeTimeProvider to both the policy and the sequence to test timeouts without waiting:

var time = new FakeTimeProvider();

Sequence<int> calls = Sequence.For<int>(time)
    .Delays(TimeSpan.FromSeconds(30))   // longer than the attempt timeout
    .Returns(1);

var policy = Resilience.Default with
{
    Time = time,
    Attempts = 1,
    AttemptTimeout = TimeSpan.FromSeconds(3),
};

Task<CallResult<int>> pending = policy.TryRunAsync(attempt => calls.NextAsync(attempt)).AsTask();
time.Advance(TimeSpan.FromSeconds(4));

CallResult<int> result = await pending;

Assert.IsType<AttemptTimeoutException>(result.Exception);

Pass the same TimeProvider to both the policy and the sequence. If the sequence uses the system clock while the policy uses a fake clock, the scripted delay becomes a real sleep.

Verify policy behavior

EventRecorder captures every CallEvent in order, so you can assert on the sequence rather than elapsed time:

var events = new EventRecorder();
Sequence<int> calls = Sequence.For<int>().Throws(new IOException()).Returns(42);

var policy = Resilience.Default with { Backoff = Backoff.None, OnEvent = events.Record };

await policy.RunAsync(attempt => calls.NextAsync(attempt));

Assert.Equal(
    [CallEventKind.Attempt, CallEventKind.Retrying, CallEventKind.Attempt, CallEventKind.Succeeded],
    events.Kinds);

Reach for a ready-made policy

TestPolicy.Instant is a Resilience value shaped for tests: three attempts, no backoff, and both the deadline and the attempt timeout set to infinite, so a test pays for neither a sleep nor a wall-clock bound it does not care about. TestPolicy.InstantHttp is the same shape with Classifier = Classifier.Http.

var api = TestPolicy.Instant;

To run Instant on a FakeTimeProvider, call TestPolicy.WithClock(time). It rebuilds any breaker the policy carries on that same clock, so the policy, its breaker and its budget all advance together:

var time = new FakeTimeProvider();
var api = TestPolicy.WithClock(time);

Test an HTTP client

Provide a scripted HttpMessageHandler as the inner handler to test a resilient HttpClient end to end. ScriptedHttpHandler serves the script you give it, then repeats the last step for every attempt after that:

var transport = new ScriptedHttpHandler()
    .Responds(HttpStatusCode.ServiceUnavailable)
    .Responds(HttpStatusCode.OK);

using HttpClient client = HttpResilience.CreateClient(
    Resilience.Http with { Backoff = Backoff.None },
    innerHandler: transport);

using HttpResponseMessage response = await client.GetAsync(new Uri("https://api.example.com/orders/1"));

Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(2, transport.CallCount);

Respond(status) and Respond(status, times) serve a fixed status code, once or for a run of attempts. Respond(response) and Respond(response, times) build a fresh HttpResponseMessage per attempt, for a response whose content a test reads. Throw(exception) and Throw(exception, times) throw instead, for the transport failures a classifier has to see.

CallCount is how many attempts reached the handler. Requests is a snapshot of what each attempt sent, in order: the method, the URI, the headers, and - only when CaptureBodies is true - the body.

Simulate a whole configuration

Simulate runs your real policy against a modeled dependency on a virtual clock and reports what it cost. Nothing sleeps, so five simulated minutes cost about what a unit test costs, and the same seed produces the same report on any machine:

SimulationReport report = Simulate.Policy(api)
    .Against(Dependency
        .Healthy(p50: TimeSpan.FromMilliseconds(20), p99: TimeSpan.FromMilliseconds(200))
        .Brownout(after: TimeSpan.FromSeconds(30), slower: 8, lasting: TimeSpan.FromMinutes(1)))
    .Under(Load.Constant(perSecond: 500))
    .For(TimeSpan.FromMinutes(5))
    .Run(seed: 42);

Assert.True(report.LoadMultiplier <= 1.2);   // attempts that reached the dependency, per call you made
Assert.True(report.Availability >= 0.9);

LoadMultiplier, Amplification, Availability, Latency(quantile), BreakerOpens, and TimeToRecover are the measurements; CountOf(kind) reaches the rest of the telemetry. Only the clock, the random source, and the dependency are modeled - the executor, the breaker, the retry budget, the classifier, and every estimator are the shipping ones.

Documentation

For more information, see the following resources:

  • Testing guide - the full walkthrough, including best practices for keeping tests fast and deterministic.
  • Simulation - modeling a dependency, offering load, and reading the report.

Feedback

Provide feedback using these channels:

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.33.0 48 9/23/2026
0.32.0 94 9/10/2026
0.31.0 95 9/9/2026
0.30.0 97 9/9/2026
0.29.0 104 9/9/2026
0.28.0 98 9/9/2026
0.27.0 93 9/9/2026
0.26.0 99 9/8/2026
0.25.0 102 9/8/2026
0.24.0 97 9/8/2026
0.23.0 100 9/5/2026
0.22.0 105 9/5/2026
0.21.0 108 9/3/2026
0.20.0 96 9/3/2026
0.19.0 99 9/3/2026
0.18.0 100 9/1/2026
0.17.0 97 9/1/2026
0.16.0 98 9/1/2026
0.15.0 91 9/1/2026
0.14.0 197 8/30/2026
Loading failed