SweetMock.Extensions.Http 0.9.41

dotnet add package SweetMock.Extensions.Http --version 0.9.41
                    
NuGet\Install-Package SweetMock.Extensions.Http -Version 0.9.41
                    
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="SweetMock.Extensions.Http" Version="0.9.41" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SweetMock.Extensions.Http" Version="0.9.41" />
                    
Directory.Packages.props
<PackageReference Include="SweetMock.Extensions.Http" />
                    
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 SweetMock.Extensions.Http --version 0.9.41
                    
#r "nuget: SweetMock.Extensions.Http, 0.9.41"
                    
#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 SweetMock.Extensions.Http@0.9.41
                    
#: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=SweetMock.Extensions.Http&version=0.9.41
                    
Install as a Cake Addin
#tool nuget:?package=SweetMock.Extensions.Http&version=0.9.41
                    
Install as a Cake Tool

SweetMock.Extensions.Http

A comprehensive extension library for mocking HttpClient and IHttpClientFactory in .NET tests using the SweetMock framework.

Features

  • Mock HttpClient: Create fully controllable HTTP client mocks for testing
  • Mock IHttpClientFactory: Mock factory instances with named and default clients
  • Fluent API: Chainable extension methods for building HTTP responses
  • Rich Response Builders: Pre-built helpers for common HTTP status codes
  • Content Helpers: Easy content creation with proper MIME types
  • Header Management: Simplified header, cache control, and content header configuration

Installation

dotnet add package SweetMock.Extensions.Http

Quick Start

HttpClient Fixture

[Fixture<HttpClientFixture>]
public class ServiceTests
{
    [Fact]
    public async Task CallApi_Success()
    {
        // Arrange
        var fixture = Fixture.HttpClientFixture(config =>
            config.client
                .Initialize(baseAddress: new Uri("http://api.local/"))
                .Send(message => message
                    .ReplyOk()
                    .WithJsonContent(new { status = "success" })
                )
        );

        var sut = fixture.CreateHttpClientFixture();

        // Act
        var result = await sut.CallRestEndpoint("query", CancellationToken.None);

        // Assert
        var call = Assert.Single(fixture.Calls.client.SendAsync());
        Assert.Equal("http://api.local/test?q=query", call.request!.RequestUri!.ToString());
    }
}

IHttpClientFactory Fixture

[Fixture<HttpClientFactoryFixture>]
public class FactoryServiceTests
{
    [Fact]
    public async Task MultipleClients_ConfiguredCorrectly()
    {
        // Arrange
        var fixture = Fixture.HttpClientFactoryFixture(config =>
        {
            config.factory.NamedClient("Client1", c =>
                c.Initialize(new Uri("http://api1.com/"))
                 .Send(m => m.ReplyOk().WithStringContent("Response 1"))
            );

            config.factory.NamedClient("Client2", c =>
                c.Send(m => m.ReplyOk().WithStringContent("Response 2"))
            );
        });

        // Act
        var sut = fixture.CreateHttpClientFactoryFixture();
        await sut.ProcessData();

        // Assert
        var client1Calls = fixture.Calls.factory.SendAsync("Client1");
        var client2Calls = fixture.Calls.factory.SendAsync("Client2");

        Assert.NotEmpty(client1Calls);
        Assert.NotEmpty(client2Calls);
    }
}

Basic HttpClient Mock

using System.Net.Http;

[Mock<HttpClient, MockOf_HttpClient>]
public class MyServiceTests
{
    [Fact]
    public async Task GetData_ReturnsExpectedContent()
    {
        // Arrange
        var httpClient = Mock.HttpClient(config =>
            config.Send(request =>
                request.ReplyOk().WithStringContent("Hello World")
            )
        );

        // Act
        var response = await httpClient.GetStringAsync("/api/data");

        // Assert
        Assert.Equal("Hello World", response);
    }
}

Advanced Request Matching

var httpClient = Mock.HttpClient(config =>
    config.Send(request => request.RequestUri!.AbsolutePath switch
    {
        "/api/users" => request.ReplyOk().WithJsonContent(new { name = "John" }),
        "/api/products" => request.ReplyOk().WithJsonContent("[{\"id\":1}]"),
        _ => request.ReplyNotFound()
    })
);

Mock IHttpClientFactory

[Mock<IHttpClientFactory, MockOf_IHttpClientFactory>]
[Mock<HttpClient, MockHttpClient>]
public class FactoryTests
{
    [Fact]
    public async Task NamedClient_WorksCorrectly()
    {
        // Arrange
        var factory = Mock.IHttpClientFactory(config =>
        {
            config.NamedClient("ApiClient", client =>
                client
                    .Initialize(new Uri("https://api.example.com/"))
                    .Send(request => request.ReplyOk().WithStringContent("API Response"))
            );

            config.DefaultClient(client =>
                client.Send(request => request.ReplyNotFound())
            );
        });

        // Act
        var client = factory.CreateClient("ApiClient");
        var response = await client.GetStringAsync("/endpoint");

        // Assert
        Assert.Equal("API Response", response);
    }
}

Response Builders

Status Code Helpers

The library provides convenient extension methods for common HTTP status codes:

// Success responses
request.ReplyOk()                    // 200 OK
request.ReplyCreated()               // 201 Created
request.ReplyNoContent()             // 204 No Content

// Redirection responses
request.ReplyMovedPermanently("/new-url")  // 301 Moved Permanently
request.ReplyFound("/temp-url")            // 302 Found
request.ReplyNotModified()                 // 304 Not Modified

// Client error responses
request.ReplyBadRequest()            // 400 Bad Request
request.ReplyUnauthorized("Bearer")  // 401 Unauthorized
request.ReplyForbidden()             // 403 Forbidden
request.ReplyNotFound()              // 404 Not Found
request.ReplyMethodNotAllowed()      // 405 Method Not Allowed
request.ReplyConflict()              // 409 Conflict
request.ReplyUnsupportedMediaType(MimeType.Json)  // 415 Unsupported Media Type
request.ReplyTooManyRequests()       // 429 Too Many Requests

// Server error responses
request.ReplyInternalServerError()   // 500 Internal Server Error
request.ReplyBadGateway()            // 502 Bad Gateway
request.ReplyServiceUnavailable()    // 503 Service Unavailable
request.ReplyGatewayTimeout()        // 504 Gateway Timeout

// Custom status code
request.Reply(HttpStatusCode.ImATeapot)  // Any status code

Content Helpers

String and Text Content

response.WithStringContent("Plain text")
response.WithHtmlContent("<html><body>Hello</body></html>")
response.WithJsonContent("{\"key\":\"value\"}")

// .NET 9+ only: Serialize objects directly
response.WithJsonContent(new { name = "John", age = 30 })

Binary Content

byte[] data = GetBinaryData();
response.WithByteArrayContent(data)  // Automatically sets Content-Length and Content-MD5

Custom Content

response.WithContent(new StringContent("data"), MimeType.Json)

Header Management

Simple Headers

response.WithHeader("X-Custom-Header", "value")
response.WithHeader("Accept-Encoding", new[] { "gzip", "deflate" })

Response Headers

response.WithHeaders(headers =>
{
    headers.ETag = new EntityTagHeaderValue("\"abc123\"");
    headers.Date = DateTimeOffset.UtcNow;
})

Cache Control Headers

response.WithCacheHeaders(cache =>
{
    cache.NoCache = true;
    cache.NoStore = true;
    cache.MaxAge = TimeSpan.FromHours(1);
})

Content Headers

response.WithContentHeaders(headers =>
{
    headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
    {
        FileName = "document.pdf"
    };
    headers.ContentType = new MediaTypeHeaderValue(MimeType.ByteStream);
})

MIME Type Constants

The MimeType class provides common MIME type constants:

MimeType.Json          // application/json
MimeType.Text          // application/text
MimeType.Form          // application/x-www-form-urlencoded
MimeType.Html          // text/html
MimeType.Css           // text/css
MimeType.JavaScript    // text/javascript
MimeType.ByteStream    // application/octet-stream

// Image types
MimeType.Image_png     // image/png
MimeType.Image_jpeg    // image/jpeg
MimeType.Image_gif     // image/gif
MimeType.Image_svg     // image/svg+xml
MimeType.Image_webp    // image/webp
MimeType.Image_avif    // image/avif
MimeType.Image_apng    // image/apng

Inspecting Mock Calls

Track and verify HTTP requests made during tests:

MockOf_IHttpClientFactory.Logs logs = null!;
var factory = Mock.IHttpClientFactory(config =>
    config
        .DefaultClient(c => c.Send(message => message.ReplyOk()))
        .GetCallLogs(out logs)
);

// Use the factory...
var client = factory.CreateClient();
await client.GetAsync("/test");

// Inspect calls
foreach (var call in logs.SendAsync())
{
    Console.WriteLine($"Request: {call.request.Method} {call.request.RequestUri}");
}

// Filter calls by client name
var specificClientCalls = logs.SendAsync("Client1",
    args => args.request!.Method == HttpMethod.Get
);

Requirements

  • .NET 8.0, 9.0, or 10.0
  • SweetMock
  • Microsoft.Extensions.Http

License

See the main SweetMock repository for license information.

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 is compatible.  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.9.41 130 5/22/2026
0.9.40 106 5/21/2026
0.9.39 142 4/29/2026
0.9.38 112 4/21/2026