Mocking dependencies for more maintainable unit tests

Mocking never looked quite as awesome!
Mocks do look quite comfortable.

Using a mocking framework in your unit tests will make your unit tests much shorter, much easier to comprehend, and much easier to maintain. In short, a good mocking framework is an absolutely must have tool in any developers toolkit.

In this article, I will discuss what a mocking framework is and why it is such a great tool for creating good unit tests. I will be using Moq in my examples, which has been my go-to framework for as long as I have been writing unit tests. I previously wrote an article about writing testable code. That is a must read before this article. What use is getting to learn about mocks if your code isn’t testable?

What are mocks?

Lets start out by explaining what a mock even is. A mock is a replacement for some dependency your classes might have. You define what it should return, how it should behave and even it’s internal state. Now that you have total control over that dependency, it’s much easier for you test classes that have that dependency.

Now there is a theoretical difference between mocks and stubs, but I don’t want to cover that. Honestly, since I’ve started working with libraries such as Moq, this question kinda of becomes irrelevant because the mocks that it provides can fulfill both roles, without me having to think about it. Now back to the matter at hand.

Imagine you have a class that is dependent on ICustomerRepository. This is your database, where you keep all your customer information. If you didn’t use mocks, you would actually be dependent on the database in order to test your class. All of a sudden, your tests are not only depending on production data, they are also affecting it! And what if you want to test how your classes behave if the database is inaccessible? Are you going to ask your DBA to shut down the database while you run your tests?

While it is true you can overcome some of the problems above with a separate testing environment, it’s still not much of an improvement. Imagine the coordination required to test all of your classes that are dependent on the database being down. Not only that, what happens when the environment is actually down? The tests fail! But wait a minute, I wasn’t testing the connection to the database, I was testing some smaller unit of work. Using testing environments are great for integration tests, but never use something you aren’t in complete control over in unit tests.

What are mocking frameworks?

They are frameworks that make this entire process of actually creating mocks much easier than they used to be. In a very, VERY short period of my life, before I learned about Moq, I used to write entire classes that fulfilled the dependencies my classes had. For example, I needed to test a class that was dependent on a ICustomerRepository. I would write a class that was something like this:


public class FakeCustomerRepository : ICustomerRepository
{
    public List<Customer> GetCustomers()
    {
        return new List<Customer> { new Customer { Name = "Eric Ruder" } };
    }
}

While this might seem okay in the beginning, it very quickly becomes an unmaintainable hell. Your interface is probably much larger than this, what about all the other methods, should they just throw NotImplemented exceptions? What if you want to see how your class behaves if the GetCustomer() method returns an empty list, or maybe throws a TimeoutException? You might slightly circumvent the problem by creating constructors that take in what the method should return. Once you realize that you now have a constructor nightmare, you might start creating multiple implementations of ICustomerRepository. 20 minutes later, you realize that now, your entire test project is a nightmare. This is where a mocking framework comes to the rescue.

Introducing: Moq

Moq is mocking framework for .net that I have been using for a long time. While there are a few alternatives, this is the most popular one out there, as well as having quite a community around it. Let’s get back to the previous example of mocking a ICustomerRepository. With Moq, I would simply do this to achieve that:

var repoMock = new Mock<ICustomerRepository>();

repoMock
    .Setup(x => x.GetCustomers())
    .Returns(new List<Customer> 
    { 
        new Customer { Name = "Eric Ruder" } 
    });

This makes it much easier to setup the behavior of the mock. You no longer have to create multiple different classes that all behave differently, depending on what you are trying to test. Not only that, but the intent behind the test is also much easier to discern. It’s right there, in the setup phase!

You can setup your mock to do all kinds of things, like throwing exceptions, setting up properties or even performing callbacks. You can even setup methods of your dependency to react differently, depending on the arguments you pass to it.

Verification

Another thing you usually need to do is to verify whether or not the class under test called it’s dependencies the way you expected it to. This is also very easy to achieve with Moq. Let’s extend our ICustomerRepository to also include a SaveCustomer method. We want to make sure that our class under test correctly calls save customer, with an object with the correct name. This is as simple as:

var repoMock = new Mock<ICustomerRepository>();

string newCustomerName = "Sven Ruder";

var classUnderTest = new CustomerManager(repoMock.Object);

classUnderTest.CreateNewCustomer(newCustomerName);

repoMock.Verify(x => x.SaveCustomer(It.Is<Customer>(c => c.Name == newCustomerName)), Times.Once());

Conclusion

I hope I’ve given you enough reason to stop creating your mocks by hand. If you want to read more about how to use Moq, I can strongly recommend reading their Quickstart guide, which should more or less cover 95% of all your mocking needs. In my next article, I will be discussing AutoFixture, which another amazing framework for writing unit tests.

1 thought on “Mocking dependencies for more maintainable unit tests

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.