How to write tests for HttpClient using Moq

This post is part of my Advent of Code.

These days I needed to unit test a service that used the built-in HttpClient. It wasn’t as easy as creating a fake for HttpClient. This is how to write tests for HttpClient with Moq and a set of extension methods to make it easier.

To write tests for a service that requires a HttpClient, create a fake for HttpMessageHandler and set up the protected SendAsync() method to return a HttpResponseMessage. Then, create a new HttpClient passing the fake instance of HttpMessageHandler created before.

How to Create a Testable HttpClient

For example, let’s write a test for a AnyService class that receives a HttpClient, using MSTest and Moq,

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

namespace MyProject.Services.Tests;

[TestClass]
public class AnyServiceTests
{
    [TestMethod]
    public async Task DoSomethingAsync_ByDefault_ReturnsSomethingElse()
    {
        var fakeHttpMessageHandler = new Mock<HttpMessageHandler>();
        fakeHttpMessageHandler
                .Protected()
                // ^^^^^^^
                .Setup<Task<HttpResponseMessage>>(
                    "SendAsync",
                    ItExpr.IsAny<HttpRequestMessage>(),
                    ItExpr.IsAny<CancellationToken>()
                )
                .ReturnsAsync(new HttpResponseMessage
                {
                    StatusCode = HttpStatusCode.OK,
                    Content = new StringContent(JsonConvert.SerializeObject(new AnyResponseViewModel()))
                    // We add the expected response here:                   ^^^^^
                });
        using var httpClient = new HttpClient(fakeHttpMessageHandler.Object);
        //                                    ^^^^^
        var service = new AnyService(client);

        var someResult = await service.DoSomethingAsync();

        // Assert something here...
        Assert.IsNotNull(someResult);
    }
}

Notice how we used the Protected() and Setup() methods from Moq to create a fake for HtttpMessageHandler. Then, inside the ReturnsAsync() method, we created a response message with a response object. And, finally, we used the fake handler to create a new HttpClient to pass it to our AnyService instance.

That’s how we created a fake HttpClient. But, as soon as we start to write more tests, all of them get bloated with lots of duplicated code. Especially, if we create new tests by copy-pasting an existing one.

We should reduce the noise in our tests using factory methods or builders to make our tests more readable. Let’s do that!

Some extensions methods to set up the faked HttpClient

It would be great if we could reduce the Arrange phase of our sample test to one or two lines. Something like this,

[TestMethod]
public async Task DoSomethingAsync_ByDefault_ReturnsSomethingElse()
{
    using var client = new Mock<HttpMessageHandler>()
                  .WithSuccessfulResponse(new AnyResponseViewModel())
                  //                      ^^^^^
                  // Alternatively,
                  // .WithUnauthorizedResponse()
                  // or
                  // .WithException<HttpRequestException>()
                  .ToHttpClient();
    var service = new AnyService(client);

    var someResult = await service.DoSomethingAsync();

    // Assert something here...
    Assert.IsNotNull(someResult);
}

It’s not that difficult to write some extension methods on top of the Mock<HttpMessageHandler> to simplify the creation of testable HttpClient instances.

In fact, here they are,

using Moq;
using Moq.Language.Flow;
using Moq.Protected;
using Newtonsoft.Json;
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace HttpMessageHandlerTests.Extensions;

public static class MockHttpMessageHandlerExtensions
{
    public static Mock<HttpMessageHandler> WithSuccessfulResponse<T>(
        this Mock<HttpMessageHandler> fakeHttpMessageHandler,
        T responseContent)
    {
        fakeHttpMessageHandler
            .GetProtectedSetup()
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent(JsonConvert.SerializeObject(responseContent))
            });

        return fakeHttpMessageHandler;
    }

    public static Mock<HttpMessageHandler> WithUnauthorizedResponse(
        this Mock<HttpMessageHandler> fakeHttpMessageHandler)
    {
        fakeHttpMessageHandler
            .GetProtectedSetup()
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.Unauthorized,
                RequestMessage = new HttpRequestMessage()
            });

        return fakeHttpMessageHandler;
    }

    public static Mock<HttpMessageHandler> WithDelegate(
        this Mock<HttpMessageHandler> fakeHttpMessageHandler,
        Func<HttpRequestMessage, CancellationToken, HttpResponseMessage> func)
    {
        fakeHttpMessageHandler
            .GetProtectedSetup()
            .ReturnsAsync(func);

        return fakeHttpMessageHandler;
    }

    public static Mock<HttpMessageHandler> WithException<TException>(
        this Mock<HttpMessageHandler> fakeHttpMessageHandler)
        where TException : Exception, new()
    {
        fakeHttpMessageHandler
            .GetProtectedSetup()
            .Throws<TException>();

        return fakeHttpMessageHandler;
    }

    public static HttpClient ToHttpClient(this Mock<HttpMessageHandler> fakeHttpMessageHandler)
    {
        return new HttpClient(fakeHttpMessageHandler.Object);
    }

    private static ISetup<HttpMessageHandler, Task<HttpResponseMessage>> GetProtectedSetup(
        this Mock<HttpMessageHandler> fakeHttpMessageHandler)
    {
        return fakeHttpMessageHandler
            .Protected()
            .Setup<Task<HttpResponseMessage>>(
                "SendAsync",
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>());
    }
}

We can add other methods like WithNotFoundResponse(), WithInternalServerResponse() or WithTooManyRequestsResponse() to cover other response codes. Even, we can setup the fake HttpMessageHandler passing an Uri with a method ForUri(), for example.

Voilà! That’s how to write tests with HttpClient and Moq. With some extension methods, we could have a small DSL to write more readable tests. For a more fully-featured alternative to write tests for HttpClient, check mockhttp, “a testing layer for Microsoft’s HttpClient library.”

If you want to read more about unit testing, check my Unit Testing 101 series where we cover from what a unit test is, to fakes and mocks, to best practices.

Happy testing!

Advent of Code 2022

This year, inspired by C# Advent and 24 Pull Requests, I decided to do my own Christmas challenge: my own Advent of Code. I prefer to call it: Advent of Posts. Starting on December 1st, I’m publishing 24 posts, one post per day.

The challenge is to write an article per day in about 2 hours, including proof-reading and banner design. I’ve written some of the post in advance to avoid content pressure.

Here is my Advent of Posts:

Happy coding!

Monday Links: 40-year Programmer, Work and Burnout

Another five interesting links I found in past weeks. This time, I found a recurring theme while reading Hacker News: burnout, caring and statisfaction.

The Forty-Year Programmer

This post contains all the insights of 40-years a lifetime working as a programmer. These are some of my favorites:

  • “Advice is expertise with all the most important bits removed.”
  • “The work is good. If it stops being good, you’ll stop too. If it stops being good, that’s an emergency: you need to take a vacation…“
  • “Don’t confuse work with your career. They’re not the same thing. They’re only barely related.”

Read full article

Developers Don’t Fight The Last War

This post reminded me about an ex-coworker that says that using the same skills, architecture, and tools is like being an architect who always design the same house over and over. What worked yesterady won’t work tomorrow, I guess.

“…Technology is changing all the time, but developers like to create software with the knowledge and skills they already have.”

Read full article

How to communicate effectively as a developer

Apart from social interaction, this is another challenge when working remotely. In the old days, we just tapped somebody else’s shoulders. These days of remote and asynchronous working, communication is more challenging. And we’re not teaching that when onboarding new hires. I still get plain “Hello” messages. I just ignore them.

This article shows a solution: “high resolution writing.” But, there’s another challenge with that. Often, we work with non-native speakers of the language used at work (pretty much, English everywhere). And, writing is a separate skill to master. Even for native speakers writing in his native language.

Read full article

Caring about jobs and enjoyment after burnout

I’ve seen an increasing amount of Hacker News posts about burnout and job-related problems. These are some of them:

Tips to relearn how to care about my job?: “I got a high paying job at a recognizable tech company, but plagued with exhaustion and lack of motivation, which is killing me daily life. Anything you guys have done to dig out of a rut?”

Has anyone managed to find enjoyment in their work after burnout?: “Has anyone come back from being burnt out to love what they do again? If so, how did you manage to do it?”

How to deal with burnout and its consequences?: Speaking about burnout: “I really don’t know how to get over this and how to move past it. I feel quite literally incapable of working…I’m trying to figure out what my future even looks like and how to move past this and any advice would be really appreciated.”

A lot of good advice in there.

What “Work” Looks Like

I think we’ve all been to those meetings where nobody cares or even listens to what the organizer says. Especially, those where the organizer reads a document, he could have shared in the first place. This article shows an alternative to collaborative brainstorming. Spoiler alert: it’s away from computers. Read full article

Voilà! Another Monday Links. Have you experience burnout? How did you overcome it? What’s different about work after burnout? What are you doing to prevent burnout?

Want to receive an email with curated links like these? Get 4 more delivered straight to your inbox every Friday. Don’t miss out on next week’s links. Subscribe to my Friday Links here.

Happy coding!

Unit Testing Principles, Practices, and Patterns: Takeaways

This book won’t teach you how to write a unit test step by step. But, it will teach you how unit testing fits the larger picture of a software project. Also, this book shows how to write integration tests and test the database. These are my takeaways.

1. What is a unit test?

“The goal of unit testing is to enable sustainable growth of the software project.” “It’s easy to fall into the trap of writing unit tests for the sake of unit testing without a clear picture of whether it helps the project.”

A successful test suite has the following properties:

  • It’s integrated into the development cycle,
  • It targets only the most important parts of your codebase: the domain model,
  • It provides maximum value with minimum maintenance costs.

A unit test is an automated test with three attributes:

  1. It verifies a small portion of behavior (a unit),
  2. does it quickly, and,
  3. in isolation from other tests

There are two groups of developers with different views about “isolation”: the London school and the Classical school.

For the London school, isolation means writing separate tests for separate classes. If a class has collaborators, we should test it using test doubles for every collaborator.

On the other hand, for the Classical school, it’s not the code that needs to be tested in isolation, but the tests. They should run in isolation from each other. It’s ok to test more than one class at a time if the tests don’t affect others by sharing state.

“A test—whether a unit test or an integration test—should be a simple sequence of steps with no branching”

A good unit test has these four attributes:

  1. Protection against regressions: “Code that represents complex business logic is more important than boilerplate code.”
  2. Resistance to refactoring: “The more the test is coupled to the implementation details of the system under test (SUT), the more false alarms it generates.”
  3. Fast feedback: “The faster the tests, the more of them you can have in the suite and the more often you can run them.”
  4. Maintainability: “How hard is to read a test” and “how hard is to run a test.”
Car cashed into a wall
That's a failing test. Photo by Gareth Harrison on Unsplash

2. What code to test?

Not all code is created equal and worth the same.

Types of Code based on complexity and number of dependencies
Types of Code by Complexity and Number of Dependencies

There are four types of code:

  1. Domain logic and algorithms: Complex code by nature
  2. Trivial code: Constructors without parameters and one-line properties
  3. Controllers: Code with no business logic that coordinates other pieces
  4. Overcomplicated code: Complex code with too many dependencies

Write unit tests for your domain model and algorithms. It gives you the best return for your efforts. Don’t test trivial code. Those tests have a close-to-zero value.

“Your goal is a test suite where each test adds significant value to the project. Refactor or get rid of all other tests. Don’t allow them to inflate the size of your test suite”

3. What is an integration test? And how to test the database?

An integration test is any test that is not a unit test. In the sense of verifying a single behavior, doing it quickly and in isolation from other tests.

Write integration tests to cover the longest happy path and use the same code that the “controllers” use.

Before writing integration tests for the database:

  • Keep the database in the source control system. It keeps track of changes and makes the code the single source of truth
  • Make reference data part of the database schema
  • Have every developer roll a separate instance
  • Use migration-based database delivery. Store your migrations in your version control system.

When writing integration tests for the database:

  • Separate database connections from transactions. Use repositories and transactions.
  • Don’t reuse database transactions or units of work between sections of the test. Integration tests should replicate the production environment as closely as possible. This means the Act part shouldn’t share connections or database context with anyone else.
  • Clean up data at the beginning of each test. Create a base class and put all the deletion scripts there.
  • Don’t use in-memory databases. They don’t have the same set of features. Use the same database system as production.
  • Extract technical, non-business-related parts into helper methods. For Arrange parts, use object mothers. And, for Assert parts, create extension methods for data assertions, like, userFromDb.ShouldExist().
  • Test only the most complex or important read operations. Forget about the rest.
  • Don’t test repositories directly. Test them as part of an overarching integration test suite.

Voilà! These are my takeaways. Although this book has “Unit Testing” in its title, I really liked it covers integration tests, especially testing the database and data-access layer. I’d say this isn’t a book for beginners. You would take more out of this book if you read The Art Of Unit Testing first.

If you want to read more about unit testing, check my Unit Testing 101 series where I cover from what unit testing is to unit testing best practices.

Want to write readable and maintainable unit tests in C#? Join my course Mastering C# Unit Testing with Real-world Examples on Udemy and learn unit testing best practices while refactoring real unit tests from my past projects. No more tests for a Calculator class.

Happy testing!

Hands-on Domain-Driven Design with .NET Core: Takeaways

If you’re new to Domain-Driven Design, this book is a good starting point. It’s a “hands-on” book. It walks through a sample marketplace for ads. It shows from what Domain-Driven Design is to how to evolve a system. Also, it contains a couple of chapters with a good introduction to Event Sourcing. These are my takeaways.

DDD and Ubiquitous Language

The main point of Domain-Driven Design (DDD) is sharing the domain language between domain experts and developers in meetings, documentation, and even in code. That’s what we call a “Ubiquitous Language.”

In our code, we should make all domain concepts explicit and express intent clearly. For example, in a software system to register Paid time off, what do StartDate, EndDate, and HalfDate mean? Does StartDate refer to the last day at work or the first non-working day? What about: FirstDayNotAtWork, CameBackToWork, and LeftDuringWorkday?

A ubiquitous language makes sense in a context. For example, a product doesn’t mean the same thing for the Sales, Purchasing, and Inventory departments.

Therefore, we should avoid God-classes like Product or Customer with properties for all possible views of the physical object, since not all properties need to be populated at a given time.

Half of a red onion on a white background
Does your architecture make you cry too? Photo by K8 on Unsplash

Onion Architecture and CQRS

This book advocate using the Onion Architecture and Command-query Responsibility Segregation (CQRS) when implementing DDD.

When following the Onion Architecture, the Domain is the center of everything, and everything depends on it. Application services and Infrastructure are layers around this core. Apart from standard libraries and some base classes, the Domain shouldn’t have any references.

“A good rule of thumb here is that the whole domain model should be testable without involving any infrastructure. Primarily, in your domain model tests, you should not use test harnesses and mocks.”

CQRS distinguishes between write and read operations using commands that mutate the system and queries that return the system state.

CQRS commands and queries
CQRS commands and queries flow

To implement CQRS, we can use database-mapped domain objects to mutate the system and SQL queries to retrieve the system, ignoring the domain model.

DDD Mechanics

To implement a domain model in code, DDD has some recognizable types of objects like entities, value objects, events, and services.

Entities should have an Id, accessible from the outside. IDs are database unique keys or GUIDs. We shouldn’t change an entity by changing its properties from outside the entity.

Value objects should be immutable. Two entities are the same by their identity but value objects by their value. To validate entity invariants, we can use a EnsureValidState() method.

Events are reactions to executions of commands. Events represent data in commands and other details from the changed entity, like the Id. Events should only contain primitive types. With events, we can notify changes in one part of the system.

Application services accept commands and use the Domain to handle the operation. An application service is responsible for translating primitive types to value objects. Often, all application services follow a similar script: they retrieve an entity from the database, mutate it and update the database.

Aggregate Roots work like a parent entity that changes its state as a whole. We should only reference, access, and manipulate child objects of an aggregate through the aggregate boundary.

Queries, Repositories, and Databases

“A domain model exists on its own, and it is designed to deal with business rules and invariants, and not to deal with the database.”

There’s a distinction between repositories and queries. Repositories deal with the aggregate state. In a ClassifiedAdRepository, we only should get ClassifiedAds. For all other data access, we should use queries.

We should write queries using the Ubiquitous Language too. For example, let’s write GetAdsPendingReview() instead of GetAds(ad => ad.State == State.PendingReview). And we can access the storage directly on our query handlers. That’s fine.

For example, this is a query to return active classified ads. We can put it inside the API layer directly,

public static class Queries
{
    public static async Task<IEnumerable<PublicClassifiedAdListItem>> QueryPublishedClassifiedAds(
        this DbConnection someDbConnection,
        QueryModels.GetPublishedClassifiedAds query)
    {
        await someDbConnection.QueryAsync<PublicClassifiedAdListItem>("Plain old SQL query",
            new
            {
                State = (int)ClassifiedAdState.Active,
                PageSize = query.PageSize,
                Offset = Offset(query.Page, query.PageSize)
            });
    }
}

I really like the simplicity of using queries instead of too many artifacts and layers of indirection to read data.

Parting Thoughts

Voilà! Those are my takeaways. I’d say it’s a good book to learn about DDD for the first time. There are things I liked and didn’t like about this book.

I liked that the book contains a practical example, a marketplace for ads, not only theory. If you want to follow along with the code sample, read these chapters: 4, 5, 6, 7, and 9. Skip most of chapter 8 if you already know how to set up EntityFramework. Skim through all others.

I liked how the sample application doesn’t use interfaces just for the sake of it. I’ve seen so many single-implementation interfaces to only use a dependency container and test it with mocks. And I also liked how query handlers use SQL statements directly instead of using another layer of indirection.

But, I didn’t like that the sample application ended up with “application services” instead of “command handlers.” I was expecting a command handler per each command and API method. The only sample application service has a huge switch statement to handle every command. Argggg!

For more takeaways, check Domain Modeling Made Functional: Takeaways. Don’t miss A case of Primitive Obsession, it shows how to put in place classes (or records) to replace primitive types. And, my heuristics to choose Value Objects.

Happing coding!