.NET HttpClient integration tests made easy.
HttpRecorder is an HttpMessageHandler
that can record and replay HTTP interactions through the standard HttpClient
. This allows the creation of HTTP integration tests that are fast, repeatable and reliable.
Interactions are recorded using the HTTP Archive format standard, so that they are easily manipulated by your favorite tool of choice.
π This is a maintained fork of the original nventive/HttpRecorder.
It includes bug fixes, support for modern .NET versions, and new features like concurrent context support.
π Table of Contents
If you're using WebApplicationFactory, this is the simplest and most powerful way to enable automatic recording and replaying across all HttpClients:
[Fact]
public async Task MyApiTest()
{
using var context = new HttpRecorderConcurrentContext((_, _) => new HttpRecorderConfiguration
{
Mode = HttpRecorderMode.Auto, // Automatically records or replays
});
var client = webAppFactory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddHttpRecorderConcurrentContextSupport(); // Injects the handler globally
});
}).CreateClient();
var response = await client.GetAsync("/api/my-endpoint");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
β No need to manually configure HttpClient
β All HttpClients use the recorder automatically
β .har files are stored per test method
β Supports parallel test execution
Install-Package HttpRecorder
If you want to apply HttpRecorder to a specific HttpClient only β without affecting the global DI container β you can use the delegating handler directly:
var interactionPath = "fixtures/test.har";
var client = new HttpClient(new HttpRecorderDelegatingHandler(interactionPath)
{
InnerHandler = new HttpClientHandler()
})
{
BaseAddress = new Uri("https://reqres.in/")
};
This is useful if you need more granular control over which clients are recorded.
π Tip: You can use CallerMemberName + CallerFilePath to automatically name har files per test.
-
Auto
(default): Replay if cassette exists, otherwise record -
Record
: Always record -
Replay
: Always replay (throws if file is missing) -
Passthrough
: Bypass recorder, make real requests
You can override the mode with the HTTP_RECORDER_MODE environment variable β useful in CI.
When replaying, HttpRecorder uses a rule-based matcher to determine which recorded response to return for a given request.
By default, if you donβt configure a matcher, it behaves exactly as if you had written:
matcher = RulesMatcher.MatchOnce
.ByHttpMethod()
.ByRequestUri(UriPartial.Path);
This means:
Requests are matched by HTTP method and the path part of the URI
Each recorded request is used once and in order
If your test sends two identical requests, both must have been recorded
If there are not enough matching requests in the .har file during replay, the test will fail.
You can customize the matching logic to match multiple times or add more rules β for example:
matcher = RulesMatcher.MatchMultiple
.ByHttpMethod()
.ByRequestUri(UriPartial.Path)
.ByHeader("Authorization");
matcher = RulesMatcher.MatchOnce
.ByHttpMethod()
.ByRequestUri(UriPartial.Path)
.ByContent(); // matches by binary content
You can also match by deserialized JSON:
matcher = RulesMatcher.MatchOnce
.ByHttpMethod()
.ByJsonContent<MyRequestDto>();
π If none of the built-in rules suit your needs, you can implement a custom IRequestMatcher.
Mask sensitive fields before saving:
var anonymizer = RulesInteractionAnonymizer.Default
.AnonymizeRequestHeader("Authorization");
You can use .har files recorded with tools like:
-
Fiddler
-
Chrome DevTools
-
Postman
Just pass the file path into the handler.
You can override how and where interactions are stored via IInteractionRepository
.
WireMock is a powerful tool, but sometimes it's more than you need β especially for simple, deterministic testing of external API calls.
Feature | WireMock | Vcr.HttpRecorder |
---|---|---|
Requires mock server | β | β |
Requires hand-written stubs | β | β (records real responses) |
Easily test external API logic | β out of the box | |
Works with WebApplicationFactory | β built-in support |