Test Driven Development in .NET Part 7: verify method arguments and exceptions with mocks using the Moq framework

In this post we’ll discuss how to verify arguments and exceptions using Moq.

Arguments

Arguments to our mock objects are important components. Therefore we should verify that the correct input parameters were passed to them. This will ensure that the right input data is used by the dependency injected into the system under test. Different parameters will also enable to us to check the behaviour of the system, e.g. by controlling the execution flow.

Open the solution we worked on in the previous post. The domain experts say that the product ID should be somehow built based on the product name. We want to extend the IProductIdBuilder interface with an overloaded BuildProductIdentifier method that accepts the Name of the product. Then we want to verify that the input parameter passed to this method is valid.

Locate When_creating_a_product.cs and add the following test:

[Test]
        public void The_product_id_should_be_created_from_product_name()
        {

        }

The Arrange phase look as follows:

//Arrange
            ProductViewModel productViewModel = new ProductViewModel() { Description = "Nice product", Name = "ProductA" };
            Mock<IProductRepository> mockProductRepository = new Mock<IProductRepository>();
            Mock<IProductIdBuilder> mockIdBuilder = new Mock<IProductIdBuilder>();
            mockIdBuilder.Setup(i => i.BuildProductIdentifier(It.IsAny<String>()));
            ProductService productService = new ProductService(mockProductRepository.Object
                , mockIdBuilder.Object); 

Note the String parameter passed to BuildProductIdentifier. Let Visual Studio create the overloaded method in the IProductIdBuilder interface. If you recall from the first post on Moq this type of setup will help verify that the BuildProductIdentifier method was called when the Create method in ProductService.cs is called.

Act is simple:

//Act
productService.Create(productViewModel);

Assert is a bit more complicated:

//Assert
mockIdBuilder.Verify(m => m.BuildProductIdentifier(It.Is<String>(n => n.Equals(productViewModel.Name, StringComparison.InvariantCultureIgnoreCase))));

Here we say that in the BuildProductIdentifier method we want to check that the parameter passed into it will be the same as the Name property of the product.

Run the tests and this new test should fail. Look at the reason in the Test Explorer window: an InvalidProductIdException was thrown. Of course we forgot to set up the expected return value of the BuildProductIdentifier method. Go ahead and replace the Setup as follows:

mockIdBuilder.Setup(i => i.BuildProductIdentifier(It.IsAny<String>())).Returns(new ProductIdentifier());

Re-run the tests and… …our new test still fails with the same message. Can you guess why? There are now two methods in IProductIdBuilder and the current Setup sets up the overloaded method whereas the current implementation of the Create method in ProductService uses the parameterless version which will return a null object. So we must pass in the product name to BuildProductIdentifier in the Create method:

product.Identifier = _productIdBuilder.BuildProductIdentifier(product.Name);

Re-run the tests and you’ll see that the new test passes but we have two failing ones: The_product_should_be_saved_if_id_was_created() and Then_repository_save_should_be_called(). The reason is the same as above: we’re setting up the wrong BuildProductIdentifier method now that we have the new policy of creating the ID from the product name. I hope you’re starting to see the real power of TDD.

Adjust the setup method in Then_repository_save_should_be_called() as follows:

mockIdBuilder.Setup(x => x.BuildProductIdentifier(It.IsAny<string>())).Returns(new ProductIdentifier());

And in The_product_should_be_saved_if_id_was_created():

mockIdBuilder.Setup(i => i.BuildProductIdentifier(productViewModel.Name)).Returns(new ProductIdentifier());

The tests should now pass.

You can even test the new test method by passing in the Description property of the Product object to BuildProductIdentifier in the Create method of ProductService. The test will fail because the expected Name property does not equal the actual value passed in.

Controlling the flow of execution

For the flow control demo we’ll work with a new object in our domain: Customer. The domain experts say that Customers must have a first and last name and should have a Status. The developers then translate the requirements into a simple code-branching mechanism when a new Customer needs to be saved: either save it ‘normally’ or in some special way depending on the customer status.

As usual we’ll take a test-first approach. Add a new folder to the Tests project called CustomerService_Tests. Add a new class called CustomerServiceTests to that folder with the first test method looking like this:

[TestFixture]
    public class When_creating_a_gold_status_customer
    {
        [Test]
        public void A_special_save_routine_should_be_used()
        {

        }
    }

We know that we’ll need some type of Repository to save the customer and also a factory that will yield the appropriate Status type based on some parameters. Insert the following Arrange section:

//Arrange
            Mock<ICustomerRepository> mockCustomerRepository = new Mock<ICustomerRepository>();
            Mock<ICustomerStatusFactory> mockCustomerStatusFactory = new Mock<ICustomerStatusFactory>();
            CustomerViewModel customerViewModel = new CustomerViewModel()
            {
                FirstName = "Elvis"
                , LastName = "Presley"
                , Status = CustomerStatus.Gold
            };

            CustomerService customerService = new CustomerService(mockCustomerRepository.Object, mockCustomerStatusFactory.Object);

Create the missing objects, constructors etc. using Visual Studio. Make sure that CustomerStatus is created as an enum type. In Act we’ll call the Create method:

//Act
customerService.Create(customerViewModel);

In the assert phase we want to verify that the SaveSpecial method is called:

//Assert
mockCustomerRepository.Verify(
                x => x.SaveSpecial(It.IsAny<Customer>()));

The test will of course fail at first so we’ll fill up our newly created objects:

Customer.cs:

public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public CustomerStatus StatusLevel { get; set; }

        public Customer(string firstName, string lastName)
        {
            FirstName = firstName;
            LastName = lastName;
        }
    }

CustomerStatus:

public enum CustomerStatus
    {
        Bronze,
        Gold,
        Platinum
    }

CustomerViewModel:

public class CustomerViewModel
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public CustomerStatus Status { get; set; }
    }

ICustomerRepository:

public interface ICustomerRepository
    {
        void SaveSpecial(Customer customer);
        void Save(Customer customer);
    }

ICustomerStatusFactory:

public interface ICustomerStatusFactory
    {
        CustomerStatus CreateFrom(CustomerViewModel customerToCreate);
    }

CustomerService:

public class CustomerService
    {
        private readonly ICustomerRepository _customerRepository;
        private readonly ICustomerStatusFactory _customerStatusFactory;

        public CustomerService(ICustomerRepository customerRepository, ICustomerStatusFactory customerStatusFactory)
        {
            _customerRepository = customerRepository;
            _customerStatusFactory = customerStatusFactory;
        }

        public void Create(CustomerViewModel customerToCreate)
        {
            var customer = new Customer(
                customerToCreate.FirstName, customerToCreate.LastName);

            customer.StatusLevel =
                _customerStatusFactory.CreateFrom(customerToCreate);

            if (customer.StatusLevel == CustomerStatus.Gold)
            {
                _customerRepository.SaveSpecial(customer);
            }
            else
            {
                _customerRepository.Save(customer);
            }
        }
    }

Re-run the tests and this new test still fails. We forgot to tell Moq that ICustomerStatusFactory should return the Gold status. Add the following the Arrange section of the test method:

mockCustomerStatusFactory.Setup(
                c => c.CreateFrom(It.Is(u => u.Status == CustomerStatus.Gold)))
                .Returns(CustomerStatus.Gold);

This means that if the Status of the CustomerViewModel is Gold then the CreateFrom of the mock object should return Gold. Run the test and you’ll see that indeed the SaveSpecial route is taken and the test passes.

Mocking exceptions

We can set up the dependencies so that they throw some specific exception when invoked. This way you can test how the system under test behaves when it encounters an exception from a dependency. We’ll simulate the following: we want the IProductIdBuilder dependency throw an exception and check if the exception is handled correctly.

Open When_creating_a_product.cs and add a new test method:

[Test]
        public void An_exception_should_be_raised_when_id_is_invalid()
        {

        }

Add the following skeleton to the method body:

//Arrange
            ProductViewModel productViewModel = new ProductViewModel() { Description = "Nice product", Name = "ProductA" };
            Mock<IProductRepository> mockProductRepository = new Mock<IProductRepository>();
            Mock<IProductIdBuilder> mockIdBuilder = new Mock<IProductIdBuilder>();
            mockIdBuilder.Setup(i => i.BuildProductIdentifier(It.IsAny<String>())).Returns(new ProductIdentifier());
            ProductService productService = new ProductService(mockProductRepository.Object
                , mockIdBuilder.Object); 

            //Act
            productService.Create(productViewModel);
            //Assert

This code should be familiar by now.

We want an exception to be thrown if the ID is incorrect. Add the following attribute to the method:

[ExpectedException(typeof(ProductCreatedException))]
        public void An_exception_should_be_raised_when_id_is_invalid()

Let VS create the ProductCreatedException class for you. Go ahead and make the new class inherit from Exception. Next we want to set up IProductIdBuilder so that it throws an ProductIdNotCreatedException when it’s called. Replace the current Setup call with the following:

mockIdBuilder.Setup(m => m.BuildProductIdentifier(It.IsAny<string>())).Throws<ProductIdNotCreatedException>();

Again, let VS create ProductIdNotCreatedException for you and have it extend the Exception object.

This should be clear: we don’t want the BuildProductIdentifier to return an ID but to throw an exception instead. Run the tests and the new test will obviously fail. The Create method does not throw any exception when there’s an unhandled exception throws from BuildProductIdentifier. Modify the Create method as follows:

public void Create(ProductViewModel productViewModel)
        {
            try
            {
                Product product = ConvertToDomain(productViewModel);
                product.Identifier = _productIdBuilder.BuildProductIdentifier(product.Name);
                if (product.Identifier == null)
                {
                    throw new InvalidProductIdException();
                }
                _productRepository.Save(product);
            }
            catch (ProductIdNotCreatedException e)
            {
                throw new ProductCreatedException(e);
            }
        }

Let VS insert a new constructor for ProductCreatedException and run the tests. They should all pass.

In the next post we’ll take a look at how to verify class properties with Moq.

Advertisements

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

3 Responses to Test Driven Development in .NET Part 7: verify method arguments and exceptions with mocks using the Moq framework

  1. crimsonfin says:

    Hi

    Just following along with this and noticed a typo in your code….

    mockCustomerStatusFactory.Setup(
                    c => c.CreateFrom(It.Is(u =>; u.Status == CustomerStatus.Gold)))
                    .Returns(CustomerStatus.Gold);

    Should be ….

    mockCustomerStatusFactory.Setup(
                    c => c.CreateFrom(It.Is(u => u.Status == CustomerStatus.Gold)))
                    .Returns(CustomerStatus.Gold);

    Enjoying this series of posts.

    Thanks

  2. crimsonfin says:

    Actually it looks like the greater and less than symbols have been stripped from my reply (replace squared brackets below) …

    mockCustomerStatusFactory.Setup(
                    c => c.CreateFrom(It.Is[CustomerViewModel](u => u.Status == CustomerStatus.Gold)))
                    .Returns(CustomerStatus.Gold);

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 )

Google+ photo

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

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

iReadable { }

.NET Tips & Tricks

Robin Sedlaczek's Blog

Developer on Microsoft Technologies

HarsH ReaLiTy

A Good Blog is Hard to Find

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

the software architecture

thoughts, ideas, diagrams,enterprise code, design pattern , solution designs

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Cyber Matters

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

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: