I’ve personally come through quite a journey when it comes to mocking in unit testing. I started (just like most people) by using mocks in almost all of my tests. Then, I went to the opposite side of the spectrum and tried to avoid mocks altogether.

Finally, I settled on mocking only one particular type of dependencies. That type is unmanaged dependencies — out-of-process dependencies, interactions which which are observable externally (SMTP service, Message bus, etc.). You can read more about it here: When to Mock.

There’s still the question of which classes (or types) to mock, exactly. In this and in the next email, I’d like to share with you the answer to this question, as well as the history around it.

As I wrote in the Unit Testing book, there are usually several classes on the path from the user input to the call to the unmanaged dependency. For example, when a UserController registers a user, it may need to send a registration confirmation email. To do that, the controller may use a helper class, which itself uses another class and so on several layers down the call stack.

You need to mock the act of sending an email (because the SMTP server is an unmanaged dependency), but precisely what class to mock?

You should mock the very last class in the chain of classes from the controller to the STMP server. This allows you to exercise the maximum amount of code and improve the test’s protection against bugs.

Another guideline here is mocking only types that you own. It was first introduced by Steve Freeman and Nat Pryce in their book Growing Object-Oriented Software, Guided by Tests.

I have a fair share of disagreements with that book (you can read about them here), but it does make several good points, and the point regarding mocking types that you own is one of them.

This guideline is about writing your own adapters on top of third-party libraries and mocking those adapters instead of the underlying types. This is beneficial because:

  • You often don’t have a deep understanding of how the third-party code works.

  • Even if that code already provides built-in interfaces, it’s risky to mock those interfaces, because you have to be sure the behavior you mock matches what the external library actually does.

  • Adapters abstract non-essential technical details of the third-party code and define the relationship with the library in your application’s terms.

So, for example, if you use a geolocation service that returns coordinates given some address, it’s best to wrap that service like this:

public async Task<Result<Coordinates>> GetCoordinatesByAddress(
    string street, string city, string zip, string state)
{
    OdsServiceResponse<GeocodedAddressListV1> latLongInfo = await _client
        .GetLocationsByAddressAsync(
            street + " " + city + " " + zip + " " + state);

    if (latLongInfo == null || latLongInfo.Result)
        return Result.Fail<Coordinates>("No latitude / longitude available for the address entered.");

    Coordinates coordinates = Coordinates.Create(
        latLongInfo.Result.GeocodedAddresses[0].Latitude,
        latLongInfo.Result.GeocodedAddresses[0].Longitude).Value;

    return Result.Ok(coordinates);
}

This is an example from a project I worked on. The field _client is of type LocationsClient, which comes from an external library. The above code itself resides in LocationsGateway, which is the wrapper on top of LocationsClient:

public class LocationsGateway : ILocationsGateway
{
    private readonly LocationsClient _client;

    public async Task<Result<Coordinates>> GetCoordinatesByAddress(
        string street, string city, string zip, string state)
}

public interface ILocationsGateway
{
    Task<Result<Coordinates>> GetCoordinatesByAddress(
        string street, string city, string zip, string state);
}

LocationsGateway is an unmanaged dependency and so in tests, I used its ILocationsGateway interface for mocking.

In theory, I could mock LocationsClient directly, but then I’d face the issues I described earlier:

  • I don’t know 100% how the library works and whether all my assumptions about its behavior are correct. Having a wrapper mitigates this uncertainty.

  • If the library changes, I’d have to update all tests (and production code) that works with it. The wrapper helps restrict such changes to one place — the wrapper itself.

Note that the guideline of mocking only types that you own is not 100% strict. The main limitation in doing otherwise is your understanding of how the external library works. If that library is trivial (to the point where its behavior is obviously simple), then mocking the library directly becomes acceptable.

Gojko Adzic has an old talk where he refined this rule of "Only mock types that you own" to "Only mock types that you understand". And I think this version better fits the intention behind the guideline.

In the next email, we’ll discuss best practices for writing wrappers on top of external services, stay tuned!

--Vlad


Enjoy this message? Here are more things you might like:

Workshops — I offer a 2-day workshop for organizations on Domain-Driven Design and Unit Testing. Reply to this email to discuss.

Unit Testing Principles, Patterns and Practices — A book for people who already have some experience with unit testing and want to bring their skills to the next level.
Learn more »

My Pluralsight courses — The topics include Unit Testing, Domain-Driven Design, and more.
Learn more »