{"id":36942131,"url":"https://github.com/dennisdoomen/mockly","last_synced_at":"2026-07-05T08:00:44.694Z","repository":{"id":319947911,"uuid":"1070604837","full_name":"dennisdoomen/mockly","owner":"dennisdoomen","description":"Fluent HTTP mocking for .NET like it should have been done","archived":false,"fork":false,"pushed_at":"2026-06-29T16:00:31.000Z","size":1220,"stargazers_count":39,"open_issues_count":9,"forks_count":9,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-07-05T05:36:07.716Z","etag":null,"topics":["dotnet","http","mocking","test-driven-development","unit-testing"],"latest_commit_sha":null,"homepage":"http://mockly.org/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dennisdoomen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/funding.yml","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"dennisdoomen","ko_fi":"dennisdoomen"}},"created_at":"2025-10-06T07:24:41.000Z","updated_at":"2026-06-29T06:42:11.000Z","dependencies_parsed_at":"2025-10-21T05:03:36.526Z","dependency_job_id":"1687d712-89a8-444b-9300-a195771ce08d","html_url":"https://github.com/dennisdoomen/mockly","commit_stats":null,"previous_names":["dennisdoomen/mockly.http","dennisdoomen/mockly"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/dennisdoomen/mockly","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dennisdoomen%2Fmockly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dennisdoomen%2Fmockly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dennisdoomen%2Fmockly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dennisdoomen%2Fmockly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dennisdoomen","download_url":"https://codeload.github.com/dennisdoomen/mockly/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dennisdoomen%2Fmockly/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35147199,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["dotnet","http","mocking","test-driven-development","unit-testing"],"created_at":"2026-01-13T10:59:27.031Z","updated_at":"2026-07-05T08:00:44.686Z","avatar_url":"https://github.com/dennisdoomen.png","language":"C#","funding_links":["https://github.com/sponsors/dennisdoomen","https://ko-fi.com/dennisdoomen"],"categories":[],"sub_categories":[],"readme":"\n\u003ch1 align=\"center\"\u003e\n  \u003cbr\u003e\n    \u003cimg src=\"./Logo.png\" style=\"width:300px\" alt=\"Mockly\"/\u003e\n  \u003cbr\u003e\n\u003c/h1\u003e\n\n\n\u003ch4 align=\"center\"\u003eFluent HTTP mocking for .NET like it should have been done\u003c/h4\u003e\n\n\n\u003cdiv align=\"center\"\u003e\n\n[![](https://img.shields.io/github/actions/workflow/status/dennisdoomen/mockly/build.yml?branch=main)](https://github.com/dennisdoomen/mockly/actions?query=branch%3amain)\n[![Coveralls branch](https://img.shields.io/coverallsCoverage/github/dennisdoomen/mockly?branch=main)](https://coveralls.io/github/dennisdoomen/mockly?branch=main)\n[![](https://img.shields.io/github/release/dennisdoomen/mockly.svg?label=latest%20release\u0026color=007edf)](https://github.com/dennisdoomen/mockly/releases/latest)\n[![](https://img.shields.io/nuget/dt/mockly.svg?label=downloads\u0026color=007edf\u0026logo=nuget)](https://www.nuget.org/packages/mockly)\n[![](https://img.shields.io/librariesio/dependents/nuget/mockly.svg?label=dependent%20libraries)](https://libraries.io/nuget/mockly)\n![GitHub Repo stars](https://img.shields.io/github/stars/dennisdoomen/mockly?style=flat)\n[![GitHub contributors](https://img.shields.io/github/contributors/dennisdoomen/mockly)](https://github.com/dennisdoomen/mockly/graphs/contributors)\n[![GitHub last commit](https://img.shields.io/github/last-commit/dennisdoomen/mockly)](https://github.com/dennisdoomen/mockly)\n[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/dennisdoomen/mockly)](https://github.com/dennisdoomen/mockly/graphs/commit-activity)\n[![open issues](https://img.shields.io/github/issues/dennisdoomen/mockly)](https://github.com/dennisdoomen/mockly/issues)\n![Static Badge](https://img.shields.io/badge/4.7.2%2C_8.0-dummy?label=dotnet\u0026color=%235027d5)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)\n![](https://img.shields.io/badge/release%20strategy-githubflow-orange.svg)\n\n\u003c/div\u003e\n\n## 📚 [Documentation](https://mockly.org/)\n\n**Visit the [official documentation website](https://mockly.org/)** for comprehensive guides, API reference, and examples.\n\n- [Quick Start](https://mockly.org/docs/quick-start) - Get started in 5 minutes\n- [Usage Guide](https://mockly.org/docs/usage) - Learn core features\n- [Advanced Features](https://mockly.org/docs/advanced) - Custom matchers, assertions, and more\n- [Building](https://mockly.org/docs/building) - Build from source\n- [Contributing](https://mockly.org/docs/contributing) - Contribution guidelines\n\n## About\n\n### What's this?\n\n**Mockly** is a powerful and flexible HTTP mocking library for .NET that makes it easy to test code that depends on `HttpClient`. It provides a fluent API for configuring HTTP request mocks, capturing request details, and asserting on HTTP interactions in your tests.\n\nThe library supports:\n* **.NET Framework 4.7.2** and higher\n* **.NET 8.0** and higher\n* **FluentAssertions 7.x and 8.x** integration for expressive test assertions\n\n### What's so special about that?\n\nUnlike other HTTP mocking libraries, Mockly offers:\n\n* **Fluent, intuitive API** - Chain method calls to build complex mocking scenarios with ease\n* **Wildcard pattern matching** - Match URLs using wildcards (`*`) in paths and query strings\n* **First-class header matching** - Match request headers, bearer tokens, and content types directly\n* **Custom matchers** - Use predicates for advanced request matching logic\n* **Request capture \u0026 inspection** - Automatically capture all requests with full metadata (headers, body, timestamp)\n* **Powerful assertions** - Built-in FluentAssertions extensions for verifying HTTP behavior\n* **Diagnostic support** - Detailed error messages when unexpected requests occur\n* **Extensibility** - Design allows for custom response generators and matchers\n* **Zero configuration** - Works out of the box with sensible defaults\n* **Performance optimized** - Regex patterns are cached for efficient matching\n* **Invocation limits** - Restrict how many times a mock can respond using `Once()`, `Twice()`, or `Times(n)`\n\n### Who created this?\n\nMockly is created and maintained by [Dennis Doomen](https://github.com/dennisdoomen), also the creator of [FluentAssertions](https://fluentassertions.com/), [PackageGuard](https://github.com/dennisdoomen/packageguard), [Reflectify](https://github.com/dennisdoomen/reflectify), [Pathy](https://github.com/dennisdoomen/pathy) and the [.NET Library Starter Kit](https://github.com/dennisdoomen/dotnet-library-starter-kit). It's designed to work seamlessly with modern .NET testing practices and integrates naturally with FluentAssertions for expressive test assertions.\n\n## Power in Simplicity\n\n```csharp\nusing var mock = new HttpMock();\n\n// 1. Match with full URL shortcuts and wildcards\n// 2. Filter by specific query parameters\n// 3. Use custom JSON options for serialization\nvar options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };\n\nmock.ForGet(\"https://api.github.com/repos/*/issues?state=open\")\n    .WithQueryParam(\"page\", \"1\")\n    .Using(options)\n    .RespondsWithJsonContent(new[] { \n        new { Id = 1, Title = \"Found a bug\" },\n        new { Id = 2, Title = \"Feature request\" }\n    });\n\n// 4. Match Bearer tokens and other headers\n// 5. Match request body using JSON equivalence\n// 6. Capture requests for later verification\nvar creations = new RequestCollection();\n\nmock.ForPost()\n    .WithPath(\"/api/users\")\n    .WithBearerToken(\"secret-token\")\n    .WithBody(new { Name = \"John\", Role = \"Admin\" })\n    .CollectingRequestsIn(creations)\n    .RespondsWithStatus(HttpStatusCode.Created);\n\n// 7. Built-in support for Problem Details (RFC 7807)\nmock.ForGet(\"/api/users/999\")\n    .RespondsWithProblemDetails(HttpStatusCode.NotFound, \"User not found\");\n\n// Get the pre-configured HttpClient and start testing!\nvar client = mock.GetClient();\n\n// 8. Assert your expectations with FluentAssertions\nmock.Should().HaveAllRequestsCalled();\ncreations.Should().ContainRequestFor(\"/api/users\")\n    .Which.HasHeader(\"X-Trace-Id\");\n```\n\n## Key Features\n\n### 🎯 Fluent Request Matching\n\n```csharp\nmock.ForGet().WithPath(\"/api/users/*\").RespondsWithJsonContent(user);\nmock.ForPost().WithPath(\"/api/data\").WithQuery(\"?filter=*\").RespondsWithStatus(HttpStatusCode.Created);\nmock.ForGet().WithPath(\"/api/secure\").WithHeader(\"X-Api-Key\").RespondsWithStatus(HttpStatusCode.OK);\nmock.ForPost().WithPath(\"/api/auth\").WithBearerToken(\"eyJ*\").RespondsWithStatus(HttpStatusCode.OK);\nmock.ForPost().WithPath(\"/api/json\").WithContentType(\"application/json\").RespondsWithStatus(HttpStatusCode.OK);\n```\n\n### 🏷️ Response Headers\n\nConfigure response headers such as `Location`, `ETag` or a custom `Content-Type`. Content headers are routed to the\nresponse content automatically; all other headers are added to the response headers.\n\n```csharp\nmock.ForPost().WithPath(\"/api/users\")\n    .RespondsWithStatus(HttpStatusCode.Created)\n    .WithHeader(\"Location\", \"/api/users/123\")\n    .WithHeader(\"ETag\", \"\\\"v1\\\"\");\n```\n\n### 📃 Clear Reporting\n\nWhen an unexpected request occurs and there are configured mocks, Mockly helps you diagnose by reporting the closest matching mock (method, scheme/host/path/query) so you can quickly see what to adjust in your setup.\n\n```\nUnexpected request to:\n  GET http://localhost/fnv_collectiveschemes(111)\n\nClosest matching mock:\n  GET https://*/fnv_collectiveschemes(123*)\n\nRegistered mocks:\n - GET https://*/fnv_collectiveschemes\n - POST https://*/fnv_collectiveschemes\n - GET https://*/fnv_collectiveschemes(123*)\n - GET https://*/fnv_collectiveschemes(123*) (1 custom matcher(s)) where (request =\u003e request.Uri?.Query == \"?$count=1\")\n - GET https://*/fnv_collectiveschemes(456)\n```\n\n### 🔍 Request Capture \u0026 Inspection\n\n```csharp\nvar patches = new RequestCollection();\nmock.ForPatch().WithPath(\"/api/update\").CollectingRequestsIn(patches);\n\n// After test execution\npatches.Count.Should().Be(3);\npatches.First().Path.Should().Contain(\"/api/update\");\n```\n\n### ✅ Powerful Assertions\n\n```csharp\nmock.Should().HaveAllRequestsCalled();\nmock.Requests.Should().NotBeEmpty();\nmock.Requests.Should().NotContainUnexpectedCalls();\n\n// Assert JSON-equivalence using a JSON string (ignores formatting/ordering)\nmock.Requests.Should().ContainRequest()\n    .WithBodyMatchingJson(\"{ \\\"id\\\": 1, \\\"name\\\": \\\"John\\\" }\");\n\n// Assert the body deserializes and is equivalent to an object graph\nvar expected = new { id = 1, name = \"John\" };\n\nmock.Requests.Should().ContainRequestFor(\"https://api.example.com/*\")\n    .WithBodyEquivalentTo(expected);\n```\n\n### 🎨 Multiple Response Types\n\n* JSON content with automatic serialization\n* Test data builder integration via `IResponseBuilder\u003cT\u003e`\n* Raw string content\n* Custom HTTP status codes\n* Custom response generators\n* OData support\n\n### 🔁 Sequenced Responses\n\nConfigure a sequence of responses for the same matched request so consecutive calls get different responses (e.g. the classic \"fail twice, then succeed\" retry test). Chain `Then(...)` after any `RespondsWith*` method. The last response repeats for any calls beyond the configured sequence.\n\n```csharp\nmock.ForGet().WithPath(\"/resource\")\n    .RespondsWithStatus(HttpStatusCode.ServiceUnavailable)\n    .Then(HttpStatusCode.ServiceUnavailable)\n    .Then(HttpStatusCode.OK);\n```\n\nThe `Then*` family mirrors the `RespondsWith*` methods (`ThenRespondsWithJsonContent`, `ThenRespondsWithContent`, `ThenRespondsWithODataResult`, and `Then(Func\u003cRequestInfo, HttpResponseMessage\u003e)`).\n\n### 🛡️ Fail-Fast Testing\n\n```csharp\nmock.FailOnUnexpectedCalls = true; // Default behavior\n// Throws UnexpectedRequestException if an unmocked request is made\n```\n\n## Quick Start\n\nInstall the package:\n\n```bash\ndotnet add package mockly\n```\n\nTo get the assertions, also install one of the two assertion packages, depending on which version of FluentAssertions you're using:\n\n```bash\ndotnet add package FluentAssertions.Mockly.v7\ndotnet add package FluentAssertions.Mockly.v8\n```\n\nBasic usage:\n\n```csharp\nusing Mockly;\nusing FluentAssertions;\n\n// Arrange\nvar mock = new HttpMock();\nmock.ForGet()\n    .WithPath(\"/api/users/123\")\n    .RespondsWithJsonContent(new { Id = 123, Name = \"John Doe\" });\n\nHttpClient client = mock.GetClient();\n\n// Act\n// Note: BaseAddress defaults to https://localhost/\nvar response = await client.GetAsync(\"/api/users/123\");\nvar content = await response.Content.ReadAsStringAsync();\n\n// Assert\nresponse.StatusCode.Should().Be(HttpStatusCode.OK);\ncontent.Should().Contain(\"John Doe\");\nmock.Should().HaveAllRequestsCalled();\n```\n\n**For complete documentation and advanced examples, visit [mockly.org](https://mockly.org/)**\n\n\u003e [!TIP]\n\u003e This project ships an [Agent Skill](https://agentskills.io) that helps AI Coding Agents use Mockly effectively. [This file](./SKILL.md) will be stored in the `.agents/skills/mockly` directory of your project when you build the project. You can disable this behavior by setting `\u003cMocklySkill\u003efalse\u003c/MocklySkill\u003e` in your project or `Directory.Build.props`.\n\n## Building\n\nTo build this repository locally, you need:\n* The [.NET SDKs](https://dotnet.microsoft.com/en-us/download/visual-studio-sdks) for .NET 4.7 and 8.0.\n* Visual Studio, JetBrains Rider or Visual Studio Code with the C# DevKit\n\nBuild using PowerShell:\n\n```bash\n./build.ps1\n```\n\nOr with the [Nuke tool](https://nuke.build/docs/getting-started/installation/):\n\n```bash\nnuke\n```\n\nFor more details, see the [Building documentation](https://mockly.org/docs/building).\n\n## Contributing\n\nYour contributions are always welcome! Please have a look at the [contribution guidelines](CONTRIBUTING.md) first.\n\nFor detailed contribution information, visit the [Contributing documentation](https://mockly.org/docs/contributing).\n\nPrevious contributors:\n\n\u003ca href=\"https://github.com/dennisdoomen/mockly/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=dennisdoomen/mockly\" alt=\"contrib.rocks image\" /\u003e\n\u003c/a\u003e\n\n(Made with [contrib.rocks](https://contrib.rocks))\n\n## Versioning\n\nThis library uses [Semantic Versioning](https://semver.org/) to give meaning to the version numbers. For the versions available, see the [releases](https://github.com/dennisdoomen/mockly/releases) on this repository.\n\n## Credits\n\nThis library wouldn't have been possible without the following tools, packages and companies:\n\n* [FluentAssertions](https://fluentassertions.com/) - Fluent API for asserting the results of unit tests by [Dennis Doomen](https://github.com/dennisdoomen)\n* [Nuke](https://nuke.build/) - Smart automation for DevOps teams and CI/CD pipelines by [Matthias Koch](https://github.com/matkoch)\n* [xUnit](https://xunit.net/) - Community-focused unit testing tool for .NET by [Brad Wilson](https://github.com/bradwilson)\n* [Coverlet](https://github.com/coverlet-coverage/coverlet) - Cross platform code coverage for .NET by [Toni Solarin-Sodara](https://github.com/tonerdo)\n* [GitVersion](https://gitversion.net/) - From git log to SemVer in no time\n* [ReportGenerator](https://reportgenerator.io/) - Converts coverage reports by [Daniel Palme](https://github.com/danielpalme)\n* [StyleCopyAnalyzer](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) - StyleCop rules for .NET\n* [Roslynator](https://github.com/dotnet/roslynator) - A set of code analysis tools for C# by [Josef Pihrt](https://github.com/josefpihrt)\n* [CSharpCodingGuidelines](https://github.com/bkoelman/CSharpGuidelinesAnalyzer) - Roslyn analyzers by [Bart Koelman](https://github.com/bkoelman) to go with the [C# Coding Guidelines](https://csharpcodingguidelines.com/)\n* [Meziantou](https://github.com/meziantou/Meziantou.Framework) - Another set of awesome Roslyn analyzers by [Gérald Barré](https://github.com/meziantou)\n\n## Related Projects\n\nYou may also be interested in:\n\n* [FluentAssertions](https://fluentassertions.com/) - The assertion library that Mockly integrates with\n* [PackageGuard](https://github.com/dennisdoomen/packageguard) - Get a grip on your open-source packages\n* [Reflectify](https://github.com/dennisdoomen/reflectify) - Reflection extensions without causing dependency pains\n* [Pathy](https://github.com/dennisdoomen/pathy) - Fluently building and using file and directory paths without binary dependencies\n* [.NET Library Starter Kit](https://github.com/dennisdoomen/dotnet-library-starter-kit) - A battle-tested starter kit for building open-source and internal NuGet libraries\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdennisdoomen%2Fmockly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdennisdoomen%2Fmockly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdennisdoomen%2Fmockly/lists"}