Test Driven Development in .NET Part 5: verifying system under test with mocks using the Moq framework

Introduction

Writing unit tests can become difficult. The following scenarios will all make it more difficult to isolate the logic you’re trying to test:

  • Complex method body with a lot of branching logic
  • Code is tightly coupled to other classes
  • Code that accesses external resources, such as databases, file systems, web services etc.

In the last two cases you may even worry about how the unit tests are going to affect the external classes and resources. The good news is that these dependencies can be abstracted away and factored out with Test Doubles or Mocks. Test Doubles are objects that provide a simplified implementation of a dependency.

Example: you have a method that accepts an IDataReader object. One concrete implementation might be called FileReader that uses the standard .NET Stream classes to read a file. In the real application the code will then open a physical file, read its contents and do something with the data. While preparing a unit test you will have to send in an IDataReader object. By sending in a FileReader you may run into the following dilemmas:

  • The true logic of the method is to process the data and not to open and read a file
  • However, you cannot test the data processing logic without an IDataReader
  • If you pass in a FileReader then you must have a valid file ready on disk with valid data
  • If the test fails, then why did it fail? Is the file missing? Is the data in the file invalid? Difficult to say
  • Normally a unit test should not directly access external resources as it takes more time and unit tests should run very fast

One solution is to write a simplified implementation of IDataReader, maybe called InMemoryDataReader that doesn’t read a physical file. It instead uses a static list of in-memory data that you verified beforehand. That way you can be sure that the data that the method under test receives is perfectly valid. Also, if the unit test fails then you’ll know that it wasn’t due to the unavailability of an external file, but a failure in the processing logic.

A similar problem arises if the method under test uses external objects and calls those objects to carry out specific tasks. A unit test that fails will again be of dubious quality. Something has failed, but we’re not sure what. With mocks we can fake those dependencies.

If you don’t use any mocking framework then you will obviously need to write your mock objects by hand. If The method under test has 4 dependencies, let’s say 4 interfaces, then you’ll need to write at least 4 mock implementations in order to write a meaningful unit test. If there’s some complicated branching logic within the method then you may even have to write additional implementations of the interfaces that adhere to the different paths the code can take. The benefit of rolling your own mock objects is that you have complete control over how the mock implementations behave and that you don’t have to learn any new framework.

The downside is that you increase the unit testing code base with every new mock object. As the unit tests are also part of the code base it also needs to be maintained – a larger code base means more code to maintain. Also, the complexity of mock objects will increase with the complexity of the code under test. An even bigger problem is the fragility of such mocks. They are subject to changes in the interfaces they implement.

Mocking with the Moq framework

Before we jump into any code there’s a pattern associated with using mocks in the unit tests that you’ll need to know: Arrange-Act-Assert, aka AAA. These are three phases within the method body of a unit test.

Arrange: set up the objects necessary to write a meaningful test on the method under test, i.e. this is an initialisation phase.
Act: execute the method under test.
Assert: verify some type of outcome of running the method

Open Visual Studio 2012 – this all should work in 2010 as well – and create a blank solution. Add two class library projects to the solution: Application.Domain and Application.Tests. Remove Class1.cs from both and add reference the Domain project in the Test project. In Application.Tests add a reference to the NUnit testing framework as outlined here. You’ll also need to add the Moq framework which is a mocking library designed for .NET3.5 and above. It can be downloaded from NuGet:

Mock in NuGet

We’ll simulate a ProductService class that creates new Products through a ProductRepository. We will test the ProductService class to demonstrate AAA and the most basic usage of a mock object. We’ll use the code first approach.
Add a folder called ProductService_Tests to the Tests project. In our first test we want to verify that the ProductRepository was called from within the ProductService when its Create method was called. Add a class called When_creating_a_product.cs to the ProductService_Tests folder and then add a method called Then_repository_save_should_be_called(). Insert the necessary NUnit attributes to come to the following starting point:

[TestFixture]
    public class When_creating_a_product
    {
        [Test]
        public void Then_repository_save_should_be_called()
        {

        }
    }

So the first stage is Arrange. We follow the standard Repository pattern so we know that CustomerService will have a dependency on ICustomerRepository to carry out the actual persistence task so we’ll need to set it up. The Moq framework uses a lot of reflection to set up mocks for us and its syntax can be strange at first with its heavy use of Lambda expressions. The most important class in Moq to build mocks is called Mock – how convenient! Add the following to the test method:

//Arrange
var mockProductRepository = new Mock<IProductRepository>();

This is the way to initialise a mock object that implements the IProductRepository interface. There’s no such interface yet, but you can create it using the techniques outlined here.

We’re not done with the Arrange phase. The interface will need to have at least one method. We decide that its method will be called Save and accept a Product object which will be saved in the repository. The next important keyword of Moq is Setup. Add the following line of code to the test method:

mockProductRepository.Setup(p => p.Save(It.IsAny<Product>()));

First make sure that you create the Save method and the Product object using the built in Visual Studio tools. Setup means setting up our expectation(s) on the mock object. We expect its Save method to be called where the Save method accepts a Product object. In Moq all expectations in the Setup method will be formed using Lambda expressions. The funny looking It.IsAny just means any type of Product object.

Next we’ll create the ProductService object which depends on an IProductRepository class:

ProductService productService = new ProductService(mockProductRepository.Object);

Let VS create ProductService and its constructor. Notice the Object property of our mock object. Moq builds a concrete instance of the mocked interface object for us on the fly and the Object property will extract this implementation. We never actually see it but we don’t care either. It takes the place of the hand-made mocks we may spend a lot of time writing.

Now comes the Act phase which means invoking the actual method we want to test:

//Act
productService.Create(new ProductViewModel());

We call the Create method of the product service which accepts a ProductViewModel object. Go ahead and create those using Visual Studio. We use the View Model to communicate with the Service from the outside. This follows the strict policy of not letting the Domain objects bubble up to the projects above the Services project. Don’t worry if you haven’t come across this way of organising code, it’s not important here. Just accept that we don’t let our domain object permeate the entire solution. It’s another discussion whether this is good or bad but we’ll definitely not pursue it any further here and now.

In the Assert phase we simply say the following: ensure that everything we expected to happen really DID happen. Add the following code:

//Assert
mockProductRepository.VerifyAll();

VerifyAll comes from the Moq framework. It means that every expectation we set up in the Arrange phase should be checked.

Go ahead and run the test with Ctrl+R, A. It should fail as we didn’t provide any implementation to the auto-generated methods and classes, there are many NotImplementedExceptions out there. Recall that a first failing test is actually good. The next stage would be Green and then Refactor but for this discussion we’ll jump ahead and implement the code as follows:

IProductRepository.cs

public interface IProductRepository
    {
        void Save(Product product);
    }

Product.cs:

public class Product
    {
        public string Name { get; set; }
        public string Description { get; set; }

        public Product(string name, string description)
        {
            Name = name;
            Description = description;
        }
    }

ProductService.cs:

public class ProductService
    {
        private IProductRepository _productRepository;

        public ProductService(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }

        public void Create(ProductViewModel productViewModel)
        {
            Product product = ConvertToDomain(productViewModel);
            _productRepository.Save(product);
        }

        private Product ConvertToDomain(ProductViewModel productViewModel)
        {
            return new Product(productViewModel.Name, productViewModel.Description);
        }
    }

ProductViewModel.cs:

public class ProductViewModel
    {
        public string Name { get; set; }
        public string Description { get; set; }
    }

Run the test again, it should pass. We can verify that Moq is not lying to us by commenting out ‘_productRepository.Save(product);’ in ProductService.Create. Run the test with that method call commented out and you should get a failing test. The Moq Verify type of assertions will test whether the system under test behaved according to our expectations.

Increasing the complexity

The previous demo showed the absolute basics of mocking with Moq. We’ll now extend the ProductRepository to be able to insert a range of Product objects. Add a new class within ProductService_Tests called When_creating_multiple_products.cs and a method called Then_product_repository_should_be_called_once_per_product():

[TestFixture]
    public class When_creating_multiple_products
    {
        [Test]
        public void Then_product_repository_should_be_called_once_per_product()
        {

        }
    }

In the Arrange phase we’ll start by building up a list of Products that will be passed into a CreateMany method of the CustomerService class:

//Arrange
            List<ProductViewModel> productViewModels = new List<ProductViewModel>()
            {
                new ProductViewModel(){Name = "ProductA", Description="Great product"}
                , new ProductViewModel(){Name = "ProductB", Description="Bad product"}
                , new ProductViewModel(){Name = "ProductC", Description="Cheap product"}
                , new ProductViewModel(){Name = "ProductD", Description="Expensive product"}
            };

And then we’ll want to verify that the Save method of the ICustomerRepository object is executed for every ProductViewModel in the list. Add the following to the test method:

var mockProductRepository = new Mock<IProductRepository>();
            mockProductRepository.Setup(p => p.Save(It.IsAny<Product>()));
            ProductService productService = new ProductService(mockProductRepository.Object);
            //Act
            productService.CreateMany(productViewModels);
            //Assert
            mockProductRepository.VerifyAll();

Add the CreateMany method to the product service using VS. You’ll notice that the code is very similar to the one we wrote above. Run the tests and the new one should of course fail due to the unimplemented CreateMany method so let’s implement it quickly:

public void CreateMany(List<ProductViewModel> productViewModels)
        {
            foreach (ProductViewModel vm in productViewModels)
            {
                _productRepository.Save(new Product(vm.Name, vm.Description));
            }
        }

Re-run the tests and they should pass. Is this enough though? The Setup method will make sure that Save will be called at least once. However, we want it to be called for each element in the Product list i.e. exactly 4 times. Remove the call to Setup and VerifyAll, we’ll use an overload of the Verify method instead:

//Assert
            mockProductRepository.Verify(p => p.Save(It.IsAny<Product>()), Times.Exactly(productViewModels.Count));

The ‘p => p.Save(It.IsAny())’ bit will be familiar from the Setup we had before. Then we specify how many times: exactly productViewModels.Count times. It is worth checking with IntelliSense what overloads of Verify exist. Run the tests and they should succeed. To extra-double-triple-check the behaviour replace ‘productViewModels.Count’ with 2 and the test should fail.

In the next post we’ll look at how to mock return values.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: