Unit testing a C# HttpClient wrapper with a fake HttpMessageHandler (xUnit, WireMock.Net)

xUnit Testing & CI/CD in .NET November 13, 2026 csharp

Don't mock HttpClient.

csharp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// ======================================================================
// Q: How do you unit test a class that uses HttpClient?
//
// Don't mock HttpClient. Give it a fake HttpMessageHandler: all of your own code
// (serialization, headers, status codes) runs as normal.
// Alternatively: WireMock.Net (a real HTTP server inside the test) for
// integration tests of the client.
// ======================================================================

public class StubHandler(HttpStatusCode status, string json) : HttpMessageHandler
{
    public HttpRequestMessage? LastRequest { get; private set; }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken ct)
    {
        LastRequest = request;
        return Task.FromResult(new HttpResponseMessage(status)
        {
            Content = new StringContent(json, Encoding.UTF8, "application/json")
        });
    }
}

[Fact]
public async Task Erp_422_becomes_ErpRejectedException()
{
    var handler = new StubHandler(HttpStatusCode.UnprocessableEntity, """{"MESSAGE":"discontinued"}""");
    var client = new ErpClient(new HttpClient(handler) { BaseAddress = new("http://erp/") });

    await Assert.ThrowsAsync<ErpRejectedException>(() => client.SendOrderAsync(order, default));
    Assert.True(handler.LastRequest!.Headers.Contains("Idempotency-Key"));
}