Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / DevOps / testing

Using the Builder Pattern to Help Your Unit Tests

5.00/5 (3 votes)
31 Jul 2021CPOL4 min read 7.7K  
Using builder pattern to help unit testing a service with more than just a couple of dependencies
In this post, you will see an example of using the builder pattern to help unit test a service with more than just a couple of dependencies

Have you ever had to unit test a service or facade with half a dozen (or more) dependencies? I’ll say it - dependency injection is great, but testing a service with too many dependencies sure is a pain in the assembly. Let me give you an example.

C#
public class UserFacade
{
    private readonly IApiService _apiService;
    private readonly IAuthService _authService;
    private readonly IDatabaseService _databaseService;
    private readonly IEmailService _emailService;
    private readonly IFileService _fileService;
    private readonly ISessionService _sessionService;

    public UserFacade(
        IApiService apiService,
        IAuthService authService,
        IDatabaseService databaseService,
        IEmailService emailService,
        IFileService fileService,
        ISessionService sessionService
    )
    {
        _apiService = apiService;
        _authService = authService;
        _databaseService = databaseService;
        _emailService = emailService;
        _fileService = fileService;
        _sessionService = sessionService;
    }

    public void SignUp(string email, string password)
    {
        var authUser = _authService.CreateUser(email, password);
        if (authUser == null)
            throw new Exception("Unable to create auth user");

        _apiService.CreateUserInApi(authUser);

        var welcomeEmailContents = _fileService.ReadFile("template.html");
        _emailService.SendEmail(email, "Welcome!", welcomeEmailContents);
    }

    public string SignIn(string email, string password)
    {
        var authUser = _authService.GetUser(email, password);
        if (authUser == null)
            throw new UnauthorizedAccessException("User does not exist.");

        _databaseService.RecordLogin(authUser);

        return _sessionService.CreateSession(authUser);
    }
}

Here, we have a fairly simple looking facade that allows a user to sign up, or sign in. It has six dependencies, which while not trivial, is not a huge number compared to what you might see in the wild. Now, how would we go about mocking those dependencies in order to write a unit test for the SignUp and SignIn methods?

At a wireframe level, that might look something like this:

C#
public class UserFacadeTests
{
    private UserFacade _sut;

    [SetUp]
    public void Setup()
    {
        // Create mocks
        var apiServiceMock = new Mock<IApiService>();
        var authServiceMock = new Mock<IAuthService>();
        var databaseServiceMock = new Mock<IDatabaseService>();
        var emailServiceMock = new Mock<IEmailService>();
        var fileServiceMock = new Mock<IFileService>();
        var sessionServiceMock = new Mock<ISessionService>();

        // Setup service under test
        _sut = new UserFacade(
            apiServiceMock.Object,
            authServiceMock.Object,
            databaseServiceMock.Object,
            emailServiceMock.Object,
            fileServiceMock.Object,
            sessionServiceMock.Object
        );
    }

    [Test]
    public void Test1()
    {
        Assert.Pass();
    }

    [Test]
    public void Test2()
    {
        Assert.Pass();
    }
}

Let’s quickly go over some basics if this looks completely foreign to you.

What are Mocks?

Mocks are on-the-fly implementations of interfaces or abstract classes that you can pass into your service for testing. In this example, I’m using a mocking library called Moq. While not shown here, Moq allows you to define callbacks and return values for properties and methods on the interface that can help verify the behaviour of the service being tested.

What’s this sut Business?

sut simply means service (or system) under test. Since your test class should only be testing one service, a common convention is to name that service sut (or _sut) to identify it as being under test.

So What’s the Problem?

Image 1

First off, you’ll probably have more than one test class for a facade of significant size - potentially one test class for each method or distinct behaviour. This means that if the constructor signature changes, you’ll have several places to update. That’s annoying.

But secondly, notice that not all dependencies are used in each operation. The SignUp method calls _authService.CreateUser, _apiService.CreateUserInApi, _fileService.ReadFile and _emailService.SendEmail. The SignIn method, on the other hand, calls _authService.GetUser, _databaseService.RecordLogin and _sessionService.CreateSession. This means that if my test class is only testing one of these two methods, I shouldn’t actually need to provide all of the service dependencies. Further though, imagine if we had two test cases - one for when _authService.GetUser returns a value, and another when it returns null (to simulate a user not found). To support this, we’d need to create a whole new instance of _sut per test case, which means a huge amount of boilerplate code for every test.

Here’s a quick illustration of what I mean.

C#
        [Test]
        public void Test_SignIn_UserFound()
        {
            _authServiceMock.Setup(m => m.GetUser(It.IsAny<string>(), It.IsAny<string>()))
                .Returns(new User()
                {
                    // ...
                });

            var sut = new UserFacade(_apiServiceMock.Object, 
                                     _authServiceMock.Object, _databaseServiceMock.Object,
                                     _emailServiceMock.Object, _fileServiceMock.Object, 
                                     _sessionServiceMock.Object);

            var sessionId = sut.SignIn("john.doe@gmail.com", "password");
            
            Assert.IsNotNull(sessionId);
        }

        [Test]
        public void Test_SignIn_UserNotFound()
        {
            _authServiceMock.Setup(m => m.GetUser(It.IsAny<string>(), It.IsAny<string>()))
                .Returns(null as User);

            var sut = new UserFacade(_apiServiceMock.Object, 
                      _authServiceMock.Object, _databaseServiceMock.Object,
                      _emailServiceMock.Object, _fileServiceMock.Object, 
                      _sessionServiceMock.Object);

            var sessionId = sut.SignIn("john.doe@gmail.com", "password");
            
            Assert.IsNull(sessionId);
        }

Keep in mind that this is a contrived example, containing only one mocked service. In a real test, you’ll probably need to mock multiple services, with possibly different behaviour for different test cases. In short, tests can turn very ugly very quickly.

Alright Einstein, What Do You Suggest?

First, let’s add a generic way for us to store dependencies on the fly.

C#
    public class DependencyManager
    {
        private readonly Dictionary<Type, object> _dependencies = 
                                                  new Dictionary<Type, object>();

        public void AddDependency<T>(T dependency)
            where T : class
        {
            _dependencies[typeof(T)] = dependency;
        }

        public T GetDependency<T>()
            where T : class
        {
            if (_dependencies.ContainsKey(typeof(T)))
            {
                return _dependencies[typeof(T)] as T;
            }

            return null;
        }
    }

I usually have a Shared test project that’s shared between all of the other test projects in a solution of significant size, which is where I would put something like this. The idea is that using this, we can register some common dependencies in the Setup stage of our test, and then register some test case specific dependencies in the test case. The same type can be registered multiple times without issue, and retrieving a dependency for a type that hasn’t been set will return null instead of producing an error. This is useful for testing code paths where a certain dependency is not required, and can in fact be null.

Next, let’s add a way for us to build an instance of the service under test (in this case, UserFacade) incrementally.

C#
public class UserFacadeBuilder
{
    private readonly  DependencyManager _dependencyManager = new DependencyManager();

    public UserFacadeBuilder AddDependency<T>(T value)
        where T : class
    {
        _dependencyManager.AddDependency(value);
        return this;
    }

    public UserFacade Build()
    {
        return new UserFacade(
            _dependencyManager.GetDependency<IApiService>(),
            _dependencyManager.GetDependency<IAuthService>(),
            _dependencyManager.GetDependency<IDatabaseService>(),
            _dependencyManager.GetDependency<IEmailService>(),
            _dependencyManager.GetDependency<IFileService>(),
            _dependencyManager.GetDependency<ISessionService>()
        );
    }
}

This component serves two purposes. Firstly, it encapsulates the newing up of the service under test into one location, so that if the constructor signature changes, we only need to change it in one place. Secondly, it provides a fluent API for registering dependencies of the service under test. Let’s see how our test class looks now.

C#
public class UserFacadeTests
{
    private UserFacadeBuilder _sutBuilder;

    [SetUp]
    public void Setup()
    {
        // Create common mocks
        var apiServiceMock = new Mock<IApiService>();
        var databaseServiceMock = new Mock<IDatabaseService>();
        var emailServiceMock = new Mock<IEmailService>();
        var fileServiceMock = new Mock<IFileService>();
        var sessionServiceMock = new Mock<ISessionService>();

        _sutBuilder = new UserFacadeBuilder()
            .AddDependency(apiServiceMock.Object)
            .AddDependency(databaseServiceMock.Object)
            .AddDependency(emailServiceMock.Object)
            .AddDependency(fileServiceMock.Object)
            .AddDependency(sessionServiceMock.Object);
    }

    [Test]
    public void Test_SignIn_UserFound()
    {
        var authServiceMock = new Mock<IAuthService>();
        authServiceMock.Setup(m => m.GetUser(It.IsAny<string>(), It.IsAny<string>()))
            .Returns(new User()
            {
                // ...
            });

        var sut = _sutBuilder
            .AddDependency(authServiceMock.Object)
            .Build();

        var sessionId = sut.SignIn("john.doe@gmail.com", "password");

        Assert.IsNotNull(sessionId);
    }

    [Test]
    public void Test_SignIn_UserNotFound()
    {
        var authServiceMock = new Mock<IAuthService>();
        authServiceMock.Setup(m => m.GetUser(It.IsAny<string>(), It.IsAny<string>()))
            .Returns(null as User);

        var sut = _sutBuilder
            .AddDependency(authServiceMock.Object)
            .Build();

        var sessionId = sut.SignIn("john.doe@gmail.com", "password");

        Assert.IsNull(sessionId);
    }
}

Now, each test case only registers a custom mock that’s specific to the test, as opposed to having to register all mocks every time. We also have the ability to omit dependencies that aren’t required for the code path that the test is concerned with. For example, since we’re only testing the SignIn method, IEmailService, IApiService and IFileService can be safely omitted from the builder.

I’ve found this pattern to be very helpful in making tests for life-size services with a realistic number of dependencies manageable.

What do you think? How do you deal with testing a service with six or more dependencies that need to be mocked? Let me know in the comments. Catch ya!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)