Swacblooms🦋

Making the Moves
Menu
  • Home
  • Motivation
  • Education
  • Programming
  • About
  • Contact
  • Privacy Policy
Home
Programming
Mocking the HttpClient in C#
Programming

Mocking the HttpClient in C#

Samson Amaugo May 5, 2022

Hello everyone, in today’s post I would be talking about how to create a mock of the HttpClient class in C# using the awesome Moq library.

The code below makes an API call that returns a todo object. The CustomLogger method evaluates the todo and logs to the Console a statement based on if the todo object has the “completed” property set to true or false.

using System.Text.Json;
using System.Text.Json.Serialization;



// Top-Level Statements
var httpClient = new HttpClient {BaseAddress = new Uri("https://jsonplaceholder.typicode.com/todos/")};
var logger = new TodoCustomLogger(httpClient, 1);
await logger.CustomLogger();

// Todo class with json attributes
public class Todo
{
    [JsonPropertyName("id")] public int Id { get; set; }

    [JsonPropertyName("userId")] public int UserId { get; set; }

    [JsonPropertyName("title")] public string Title { get; set; } = "";

    [JsonPropertyName("completed")] public bool Completed { get; set; }
}

public class TodoCustomLogger
{
    private readonly HttpClient _client;

    public TodoCustomLogger(HttpClient client, int id)
    {
        _client = client;
        Id = id;
    }

    private int Id { get; }

    public async Task<string> CustomLogger()
    {
        var response = await _client.GetAsync(Convert.ToString(Id));
        var responseBody = await response.Content.ReadAsStringAsync();
        var deserialized = JsonSerializer.Deserialize<Todo>(responseBody)!;
        return deserialized.Completed
            ? $"Your todo: {deserialized.Title} has been completed "
            : $"Your todo: {deserialized.Title} is yet to be completed ";
    }
}

When writing a unit test you might consider mocking the HttpClient class using the Moq library. The problem you’ll encounter is that you can’t create a mock of the HttpClient class and set up expectations.

This is because the HttpClient members don’t have the virtual accessor to override them and there is no interface like IHttpClient to implement it at the time of writing this.

The good thing is that the HttpClient class constructor has an overload that allows you to pass in a handler.

The handler allows you to configure the HttpClient class. You can configure the cookies, headers, and responses with the handler.

So with the handler, when writing the unit tests all I need to do is to configure the handler to my taste and pass it into the HttpClient, for full control over the system under test.

The code below is a custom handler:

public class CustomHandler : HttpClientHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        return Task.FromResult(new HttpResponseMessage
        {
            Content = new StringContent(
                "{ 'userId': 1,  'id': 38,  'title': 'fugiat veniam minus',   'completed': false }")
        });
    }
}

So if I pass in the “CustomHandler” class into the constructor of the HttpClient class I would be assured that the HttpClient would return the object below:

{
  "userId": 1,
  "id": 38,
  "title": "fugiat veniam minus",
  "completed": false
}
// Here is how to configure the HttpClient class with the handler
var client = new HttpClient(new CustomHandler())

To mock the HttpClientHandler using the Moq’s library you would have to use the protected method of the Mock class because the SendAsync method or the Send method in the HttpClientHandler is protected.

var todo = new Todo()
{
    UserId = 1,
    Id = 38,
    Title = "code",
    Completed = false
};
HttpResponseMessage responseMessage = new HttpResponseMessage
{   //The StringContent class creates the HTTP body and content headers
    Content = new StringContent(JsonSerializer.Serialize(todo))
};
var moq = new Mock<HttpClientHandler>();
moq.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
    ItExpr.IsAny<CancellationToken>()).ReturnsAsync(responseMessage);
var handler = moq.Object;
var client = new HttpClient(handler);

I used the ItExpr instead of the It class to match the input because the “SendAsync” is a protected class.

To know more about mocking a protected class using the Moq library check this link.

Below is the full Test class :

using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Moq.Protected;

//using Program;

namespace Tests;

[TestClass]
public class TodoCustomLoggerTests
{
    private readonly string baseUri = "https://jsonplaceholder.typicode.com/todos/";


    [TestMethod]
    public void LogTodoIsCompleted()
    {
        //arrange
        var todo = new Todo
        {
            UserId = 1,
            Id = 38,
            Title = "code",
            Completed = true
        };
        var responseMessage = new HttpResponseMessage
        {
            Content = new StringContent(JsonSerializer.Serialize(todo))
        };
        var moq = new Mock<HttpClientHandler>();
        moq.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
            ItExpr.IsAny<CancellationToken>()).ReturnsAsync(responseMessage);
        var handler = moq.Object;
        var client = new HttpClient(handler);
        client.BaseAddress = new Uri(baseUri);
        //act
        var logger = new TodoCustomLogger(client, 1);


        //assert
        var expected = $"Your todo: {todo.Title} has been completed ";
        Assert.AreEqual(expected, logger.CustomLogger().Result);
    }

    [TestMethod]
    public void LogTodoIsNotCompleted()
    {
        //arrange
        var todo = new Todo
        {
            UserId = 1,
            Id = 38,
            Title = "code",
            Completed = false
        };
        var responseMessage = new HttpResponseMessage
        {
            Content = new StringContent(JsonSerializer.Serialize(todo))
        };
        var moq = new Mock<HttpClientHandler>();
        moq.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
            ItExpr.IsAny<CancellationToken>()).ReturnsAsync(responseMessage);
        var handler = moq.Object;
        var client = new HttpClient(handler);
        client.BaseAddress = new Uri(baseUri);
        //act
        var logger = new TodoCustomLogger(client, 1);


        //assert
        var expected = $"Your todo: {todo.Title} is yet to be completed ";
        Assert.AreEqual(expected, logger.CustomLogger().Result);
    }
}

The link to the repository of the code above can be seen here.
Thanks for reading 😅

Prev Article
Next Article

Related Articles

teaching with less practicality
We all have our funny, ridiculous, silly, or inspiring story …

Learning to Code

IntelliSense in C#
In this video, I explain how to ensure that your …

IntelliSense In C# using VsCode (OmniSharp)

About The Author

Samson Amaugo

I am Samson Amaugo. I am a full-stack developer and I specialize in DotNet and the MERN stack.

Leave a Reply

Cancel reply

Search Site

Recent Posts

  • Running Entity Framework Migrations in an Aspire-Bootstrapped Orleans Project
  • Using XDebug in Laravel Sail (VSCODE)
  • Custom Redis Streams Provider for Orleans
  • Creating a Custom File Storage Provider for Microsoft Orleans
  • Incremental Generators In C#

Categories

  • EDUCATION
  • Motivation
  • Programming
  • Uncategorized

Get more stuff

Subscribe to our mailing list and get interesting stuff and updates to your email inbox.

Thank you for subscribing.

Something went wrong.

we respect your privacy and take protecting it seriously

RSS Swacblooms

  • Running Entity Framework Migrations in an Aspire-Bootstrapped Orleans Project
  • Using XDebug in Laravel Sail (VSCODE)
  • Custom Redis Streams Provider for Orleans
  • Creating a Custom File Storage Provider for Microsoft Orleans
  • Incremental Generators In C#
  • Hot Chocolate Data Loader: A Quick Guide
  • Exploring Policy-Based Authorization in Minimal API with .NET 8
  • Using Cloud Firestore in Blazor WebAssembly
  • Serving A VueJs App from DotNet
  • Dynamic Subscriptions in Hot Chocolate 🔥🍫

Swacblooms🦋

Making the Moves
Copyright © 2025 Swacblooms🦋
Swacblooms - Making the Moves