{"id":21730067,"url":"https://github.com/nventive/httprecorder","last_synced_at":"2025-04-13T00:14:36.329Z","repository":{"id":57863024,"uuid":"197415526","full_name":"nventive/HttpRecorder","owner":"nventive","description":".NET HttpClient integration tests made easy.","archived":false,"fork":false,"pushed_at":"2025-03-31T03:11:31.000Z","size":66,"stargazers_count":29,"open_issues_count":4,"forks_count":9,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-13T00:14:31.795Z","etag":null,"topics":["csharp","httpclient","netstandard","netstandard20"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nventive.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-17T15:24:52.000Z","updated_at":"2025-04-11T11:25:16.000Z","dependencies_parsed_at":"2024-06-21T14:14:48.499Z","dependency_job_id":"96c207ec-8a3a-4d20-9385-7dc24da1bb91","html_url":"https://github.com/nventive/HttpRecorder","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FHttpRecorder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FHttpRecorder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FHttpRecorder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FHttpRecorder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nventive","download_url":"https://codeload.github.com/nventive/HttpRecorder/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248647274,"owners_count":21139086,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","httpclient","netstandard","netstandard20"],"created_at":"2024-11-26T04:11:42.408Z","updated_at":"2025-04-13T00:14:36.307Z","avatar_url":"https://github.com/nventive.png","language":"C#","readme":"# HttpRecorder\n\n.NET HttpClient integration tests made easy.\n\nHttpRecorder 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.\n\nInteractions are recorded using the [HTTP Archive format standard](https://en.wikipedia.org/wiki/.har), so that they are easily manipulated by your favorite tool of choice.\n\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)\n[![Build Status](https://dev.azure.com/nventive-public/nventive/_apis/build/status/nventive.HttpRecorder?branchName=master)](https://dev.azure.com/nventive-public/nventive/_build/latest?definitionId=3\u0026branchName=master)\n![Nuget](https://img.shields.io/nuget/v/HttpRecorder.svg)\n\n## Getting Started\n\nInstall the package:\n\n```\nInstall-Package HttpRecorder\n```\n\nHere is an example of an integration tests using **HttpRecorder** (the `HttpRecorderDelegatingHandler`):\n\n```csharp\nusing System;\nusing System.IO;\nusing System.Net;\nusing System.Net.Http;\nusing System.Runtime.CompilerServices;\nusing System.Threading.Tasks;\nusing HttpRecorder;\nusing Xunit;\n\nnamespace Sample\n{\n    public class SampleIntegrationTests\n    {\n        [Fact]\n        public async Task ItShould()\n        {\n            // Initialize the HttpClient with the recorded file\n            // stored in a fixture repository.\n            var client = CreateHttpClient();\n\n            // Performs HttpClient operations.\n            // The interaction is recorded if there are no record,\n            // or replayed if there are\n            // (without actually hitting the target API).\n            // Fixture is recorded in the SampleIntegrationTestsFixtures\\ItShould.har file.\n            var response = await client.GetAsync(\"api/user\");\n\n            // Performs assertions.\n            Assert.Equal(HttpStatusCode.OK, response.StatusCode);\n        }\n\n        private HttpClient CreateHttpClient(\n            [CallerMemberName] string testName = \"\",\n            [CallerFilePath] string filePath = \"\")\n        {\n            // The location of the file where the interaction is recorded.\n            // We use the C# CallerMemberName/CallerFilePath attributes to\n            // automatically set an appropriate path based on the test case.\n            var interactionFilePath = Path.Join(\n                Path.GetDirectoryName(filePath),\n                $\"{Path.GetFileNameWithoutExtension(filePath)}Fixtures\",\n                testName);\n\n            // Initialize the HttpClient with HttpRecorderDelegatingHandler, which\n            // records and replays the interactions.\n            // Do not forget to set the InnerHandler property.\n            return new HttpClient(\n                new HttpRecorderDelegatingHandler(interactionFilePath) { InnerHandler = new HttpClientHandler() })\n            {\n                BaseAddress = new Uri(\"https://reqres.in/\"),\n            };\n        }\n    }\n}\n```\n\n## Features\n\n### Record mode\n\nThe  `HttpRecorderDelegatingHandler` can be run in different modes:\n\n- Auto: Default mode - replay the interactions if the recording exists, otherwise record it.\n- Record: Always record the interaction, even if a record is present.\n- Replay: Always replay the interaction, throw if there is no recording.\n- Passthrough: Always passes the request/response down the line, without any interaction\n\nJust use the appropriate mode in the `HttpRecorderDelegatingHandler`  constructor.\n\nThe mode can also be overridden using the environment variable `HTTP_RECORDER_MODE`.\nIf this is set to any valid `HttpRecorderMode` value, it will override the mode set in the code,\nexcept if this mode is `HttpRecorderMode.Passthrough`.\nThis is useful when running in a CI environment and you want to make sure that no\nrequest goes out and all interactions are properly committed to the codebase.\n\n### Customize the matching behavior\n\nBy default, matching of the recorded requests is done by comparing the HTTP Method and complete Request URI. The first request that match is used and will not be returned again in the current run.\n\nIf needed, the matching behavior can be customized using the `RulesMatcher`:\n\n```csharp\nusing HttpRecorder.Matchers;\n\n// Will match requests once in order, without comparing requests.\nvar matcher = RulesMatcher.MatchOnce;\n\n// Will match requests once only by comparing HTTP methods.\nmatcher = RulesMatcher.MatchOnce.ByHttpMethod();\n\n// Will match requests multiple times by comparing HTTP methods,\n// request uri (excluding the query string) and the X-API-Key header.\nmatcher = RulesMatcher.MatchMultiple\n    .ByHttpMethod()\n    .ByRequestUri(UriPartial.Path)\n    .ByHeader(\"X-API-Key\");\n\n// Custom matching rule using the provided incoming request\n// and a recorded interaction message.\nmatcher = RulesMatcher.MatchOnce.By((request, message) =\u003e ...);\n\nvar client = new HttpClient(new HttpRecorderDelegatingHandler(\"...\", matcher: matcher));\n```\n\nAdditional customization can be done by providing a custom `IRequestMatcher` implementation.\n\n### Anonymize the records\n\nSometimes, there are portions of the requests / responses that you don't want recorded\n(e.g. because of API keys you do not want to commit to the source code repo...).\n\nIn this case, you can use the `RulesInteractionAnonymizer` to perform the substitution.\n\n```csharp\nusing HttpRecorder.Anonymizers;\n\nvar anonymizer = RulesInteractionAnonymizer.Default\n    .AnonymizeRequestQueryStringParameter(\"queryStringParam\")\n    .AnonymizeRequestHeader(\"requestHeader\");\n\nvar client = new HttpClient(new HttpRecorderDelegatingHandler(\"...\", anonymizer: anonymizer));\n```\n\nAdditional customization can be done by providing a custom `IInteractionAnonymizer`\nimplementation.\n\n### HttpClientFactory\n\nThe component comes with extension methods for the HttpClientFactory:\n\n```csharp\nservices\n    .AddHttpClient(\"TheClient\")\n    .AddHttpRecorder(interactionName);\n```\n\n### Recorder Context\n\nIt is sometime helpful to be able to decoralate the injection of the `HttpRecorderDelegatingHandler`\nand the Test case setup.\n\nThis is especially useful in the context of ASP.NET Core Integration tests.\n\nIt is possible to add the `HttpRecorderDelegatingHandler` globally to all `HttpClient` managed by the `IHttpClientFactory`,\nand then to customize the recording in the test case by using the `HttpRecorderContext`.\n\nHere is how to do it:\n\n```csharp\n// When registering the services, do the following:\nservices.AddHttpRecorderContextSupport();\n// This can be done in the ConfigureWebHost method of the WebApplicationFactory for example.\n// It will inject the HttpRecorderDelegatingHandler in all HttpClients.\n\n// Then, write your test cases using the following pattern:\n[Fact]\npublic async Task ItShould()\n{\n    using var context = new HttpRecorderContext(); // Notice the using pattern here.\n    // .. Perform test case. Interactions are recorded and replay as expected :-)\n}\n\n// Additional configuration per HttpClient can be setup as well:\n[Fact]\npublic async Task ItShould()\n{\n    using var context = new HttpRecorderContext((sp, builder) =\u003e\n        {\n            return builder.Name switch // The builder name here is the name of the HttpClient.\n            {\n                nameof(TypedClient) =\u003e new HttpRecorderConfiguration\n                {\n                    Matcher = RulesMatcher.MatchMultiple,\n                },\n                nameof(DisabledClient) =\u003e new HttpRecorderConfiguration\n                {\n                    Enabled = false,\n                },\n                _ =\u003e null // Default configuration.\n            };\n        });\n    // .. Perform test case.\n}\n```\n\n### Record interaction in external tools\n\nInteraction files can be recorded using your favorite tool (e.g. [Fiddler](https://www.telerik.com/fiddler), Google Chrome Inspector, ...).\n\nYou only have to export it using the HAR/HTTP Archive format. They can then be used as-is as a test fixture that will be loaded by the `HttpRecorderDelegatingHandler`.\n\n### Customize the storage\n\nReading/writing the interaction can be customized by providing a custom `IInteractionRepository` implementation.\n\n## Changelog\n\nPlease consult the [CHANGELOG](CHANGELOG.md) for more information about version\nhistory.\n\n## License\n\nThis project is licensed under the Apache 2.0 license - see the\n[LICENSE](LICENSE) file for details.\n\n## Contributing\n\nPlease read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the process for\ncontributing to this project.\n\nBe mindful of our [Code of Conduct](CODE_OF_CONDUCT.md).\n\n## Acknowledgments\n\n- https://github.com/vcr/vcr\n- https://github.com/nock/nock\n- https://github.com/mleech/scotch\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnventive%2Fhttprecorder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnventive%2Fhttprecorder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnventive%2Fhttprecorder/lists"}