Moq

Overview
  • Introduction to the Moq framework
  • Introduction to Mocking
  • Hand Rolled Mocks
  • Mocking with Moq
  • Advanced Mocking with Moq
Introduction to the Moq framework

Supported Frameworks

  • .NET 3.5 and higher
  • Silverlight
  • Project containing tests can not be "Client Profile"

Installation: install-package Moq

Introduction to Mocking

Overview

  • Unit testing can be difficult
    • Complex Code
    • Code that is Coupled to other classes
    • Code that accesses external resources
  • Good News... Test Doubles can help!
  • Test Doubles help you isolate the code you want to test
  • Test doubles help support good coding principles

Properties of a good unit test

  • Atomic(one small piece of functionality)
  • Deterministic(should always either pass or fail)
  • Repeatable
  • Order Independent and isolated(your test should not need to be run in any specific order to pass)
  • Fast
  • Easy to setup

A common problem with unit tests: A method under test can have instances of several other methods and objects. And those methods and objects can have several other methods and classes.

This can be avoided when all the dependencies of the method under test has fake dependencies. This is where test doubles come into play

Hand Rolled Mock Objects
  • These are fake implementations of different abstraction that the method under test requires. For example: CustomerService class that implements the ICustomerService interface. To create a mock object you might create the MockCustomerService class dn implement ICustomerService interface.
  • Once you've got the mock object created, you can now start to test that the code that needs to call that abstracted ICustomerService interface code is actually doing the calls. You can also work with it in different ways as far as providing back return values to control flow and exceptions so that you can test that exception handling is working correctly and things like that. To create those hand rolled mock objects you have to do all of the coding in hand. So you need to create the class by hand, you need to implement the interface that it needs by hand. And you need to implement the methods and properties that are on that interface, so that theywill be able to work in your test in a way that you need them to

Creating hand rolled mocks

  • Basic Class
  • Implement the dependency interface
  • Flush out the dependency functionality needed, the methods and properties.
    • Return values
    • Exception throwing
    • "Was it called?"
    • "How many times was it called?"

Sample code



namespace Logger
{
    public interface IFooterLogger
    {
        string LogFoot();
    }
}


namespace Logger
{
    public interface IHeaderLogger
    {
        string LogHead();
    }
}

namespace Logger
{
    public interface ILogger
    {
        void Log();
    }
}

namespace Logger
{
    public interface IScrubbingLogger
    {
        void ScrubLog();
    }
}

namespace Logger
{
    public class Logger
    {

        private IHeaderLogger _headerLogger;
        private IFooterLogger _footerLogger;
        private IScrubbingLogger _scrubbingLogger;
        private ILogger _logger;

        public Logger(IHeaderLogger headerLogger, IFooterLogger footerLogger, IScrubbingLogger scrubbingLogger, ILogger logger)
        {
            _headerLogger = headerLogger;
            _footerLogger = footerLogger;
            _scrubbingLogger = scrubbingLogger;
            _logger = logger;
        }

        public void LogMessage(string message)
        {
            _headerLogger.LogHead();
            _scrubbingLogger.ScrubLog();
            _logger.Log();
            _footerLogger.LogFoot();
        }
        
    }
}

Test


using Logger;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Diagnostics;

namespace LoggerTest
{

    public class MockScrubber : IScrubbingLogger
    {
        public void ScrubLog()
        {
            FromWasCalled = true;
        }

        public bool FromWasCalled { get; private set; }
    }

    public class MockHeaderLogger : IHeaderLogger
    {
        public string LogHead()
        {
            Debug.Print("HeaderLogger");
            return String.Empty;
        }
    }

    public class MockFooterLogger : IFooterLogger
    {

        public string LogFoot()
        {
            Debug.Print("FooterLogger");
            return String.Empty;
        }
    }

    public class MockLogger : ILogger
    {
        public void Log()
        {
            Debug.Print("Logger");
        }
    }



    [TestClass]
    public class LoggerClassTests
    {
        private MockScrubber _mockScrubber;
        private MockHeaderLogger _mockHeaderLogger;
        private MockFooterLogger _mockFooterLogger;
        private MockLogger _mockLogger;
        [TestInitialize]
        public void Setup()
        {
            _mockScrubber = new MockScrubber();
            _mockHeaderLogger = new MockHeaderLogger();
            _mockFooterLogger = new MockFooterLogger();
            _mockLogger = new MockLogger();
            var logger = new Logger.Logger(_mockHeaderLogger, _mockFooterLogger, _mockScrubber, _mockLogger);
            logger.LogMessage("my message");
        }

        [TestMethod]
        public void sensitivedatashouldbescrubbedfromthelogmessage()
        {
            Assert.IsTrue(_mockScrubber.FromWasCalled);
        }
    }
}

The Good

  • Complete control over mock object functionality
  • No need to learn frameworks
  • As complexity grows, so do mock objects(can indicate that may be you have broken single responsibility principle for your code under test)

The Bad

  • Each mock object adds more code
  • As complexity grows, so do mock objects(sometimes complexity is required.)
    • More hand rolled mock objects
    • or More complex mock objects

The Ugly

  • Brittleness when code under test changes
    • Interface changes
    • Interaction logic changes

Mocking with Moq

Outline

  • AAA
  • Verification
  • Return Values
  • Parameters
  • Exceptions
  • Properties
  • Stubbing
  • Events

AAA

Arrange, Act, Assert (see test/unittesting)

  • Arrange
    • Creating a mock object
    • Pass the mock to the SUT(system under test) or CUT(code under test)
  • Act
    • Execute the SUT
  • Assert
    • Verify the SUT's interaction with the mock object


using System;
using System.Collections.Generic;

namespace Customer
{
    public class CustomerService
    {
        private readonly ICustomerRepository _customerRepository;

        public CustomerService(ICustomerRepository customerRepository)
        {
            _customerRepository = customerRepository;
        }

        public void Create(CustomerToCreateDto customerToCreateDto)
        {
            var customer = BuildCustomerObjectFrom(customerToCreateDto);
            _customerRepository.Save(customer);
        }

        private Customer BuildCustomerObjectFrom(CustomerToCreateDto customerToCreateDto)
        {
            return new Customer(customerToCreateDto.Name, customerToCreateDto.City);
        }
    }

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

    public class CustomerToCreateDto
    {
        public String Name { get; set; }
        public String City { get; set; }
    }

    public class Customer
    {
        public Customer(String name, String city)
        {
            Name = name;
            City = city;
        }

        public String Name { get; set; }
        public String City { get; set; }
    }

    public class CustomerRepository : ICustomerRepository
    {
        List<Customer> customers;
        public CustomerRepository()
        {
            customers = new List<Customer>();
        }
        public void Save(Customer customer)
        {
            customers.Add(customer);
        }
    }
}

The Test


using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace Customer.UnitTests
{
    [TestClass]
    public class CustomerTests
    {
        [TestMethod]
        public void the_repository_save_should_be_called()
        {
            //Arrange
            var mockRepository = new Mock<ICustomerRepository>();

            mockRepository.Setup(x => x.Save(It.IsAny<Customer>()));

            var customerService = new CustomerService(mockRepository.Object);


            //Act
            customerService.Create(new CustomerToCreateDto());

            //Assert
            mockRepository.VerifyAll();
        }
    }
}

To verify the number of times Save is called.


using System;
using System.Collections.Generic;

namespace Customer
{
    public class CustomerService
    {
        private readonly ICustomerRepository _customerRepository;

        public CustomerService(ICustomerRepository customerRepository)
        {
            _customerRepository = customerRepository;
        }

        public void Create(IEnumerable<CustomerToCreateDto> customersToCreate)
        {
            foreach (var customerToCreateDto in customersToCreate)
            {
                _customerRepository.Save(new Customer(customerToCreateDto.Name, customerToCreateDto.City));
            }
        }

        
    }

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

    public class CustomerToCreateDto
    {   
        public String City { get; set; }
        public String FirstName { get; set; }
        public String LastName { get; set; }
        public String Name { get { return FirstName + " " + LastName; } }
    }

    public class Customer
    {
        public Customer(String name, String city)
        {
            Name = name;
            City = city;
        }

        public String Name { get; set; }
        public String City { get; set; }
    }

    public class CustomerRepository : ICustomerRepository
    {
        List<Customer> customers;
        public CustomerRepository()
        {
            customers = new List<Customer>();
        }
        public void Save(Customer customer)
        {
            customers.Add(customer);
        }
    }
}

The test


 [TestMethod]
        public void the_customer_repository_should_be_called_once_per_customer()
        {
            //Arrange
            var listOfCutomerDtos = new List<CustomerToCreateDto>
            {
                new CustomerToCreateDto{FirstName="Sam", LastName="Sampson"},
                new CustomerToCreateDto{FirstName="Bob", LastName="Builder"},
                new CustomerToCreateDto{FirstName="Doug", LastName="Digger"},
            };

            var mockCustomerRepository = new Mock<ICustomerRepository>();

            //This Setup only checks that the method Save is being called at least once.
            //mockCustomerRepository.Setup(x => x.Save(It.IsAny<Customer>()));
            
            var customerService = new CustomerService(mockCustomerRepository.Object);


            //Act
            customerService.Create(listOfCutomerDtos);

            //Assert
            //mockCustomerRepository.Verify();
            mockCustomerRepository.Verify(x => x.Save(It.IsAny<Customer>()), Times.Exactly(listOfCutomerDtos.Count));
        }

This is to check for return values. In first method we specify that null will be returned and in the second method we specify that string will be returned. And based on these return types, check if a particular method is called or not.


using System;
using System.Collections.Generic;

namespace Customer
{
    public class CustomerService
    {
        private readonly ICustomerRepository _customerRepository;
        private readonly ICustomerAddressBuilder _customerAddressBuilder;

        public CustomerService(ICustomerRepository customerRepository, ICustomerAddressBuilder customerAddressBuilder)
        {
            _customerRepository = customerRepository;
            _customerAddressBuilder = customerAddressBuilder;
        }

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

            customer.MailingAddress = _customerAddressBuilder.From(customerToCreateDto);

            if (customer.MailingAddress == null)
            {
                throw new InvalidCustomerMailingAddressException();
            }

            _customerRepository.Save(customer);
        }

        
    }

    public class InvalidCustomerMailingAddressException : Exception
    {
        public InvalidCustomerMailingAddressException() : base("Invalid Address")
        {
        }
    }


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

    public interface ICustomerAddressBuilder
    {
        String From(CustomerToCreateDto customerToCreateDto);
    }


    public class CustomerToCreateDto
    {   
        public String City { get; set; }
        public String FirstName { get; set; }
        public String LastName { get; set; }
        public String Name { get { return FirstName + " " + LastName; } }
    }

    public class Customer
    {
        public Customer(String firstName, String lastName)
        {
            Name = firstName + " " + lastName;
        }

        public String Name { get; set; }
        public String City { get; set; }
        public String MailingAddress { get; set; }
    }

    public class CustomerRepository : ICustomerRepository
    {
        List<Customer> customers;
        public CustomerRepository()
        {
            customers = new List<Customer>();
        }
        public void Save(Customer customer)
        {
            customers.Add(customer);
        }
    }

    public class CustomerAddressBuilder : ICustomerAddressBuilder
    {
        public string From(CustomerToCreateDto customerToCreateDto)
        {
            return "Address is " + customerToCreateDto.City;
        }
    }

}

The test


[TestMethod]
        [ExpectedException(typeof(InvalidCustomerMailingAddressException))]
        public void an_exception_should_be_thrown_if_the_address_is_not_created()
        {
            //Arrange
            var customerToCreateDto = new CustomerToCreateDto { FirstName = "Bob", LastName = "builder" };

            var _mockCustomerRepository = new Mock<ICustomerRepository>();
            var _mockCustomerAddressBuilder = new Mock<ICustomerAddressBuilder>();

            _mockCustomerAddressBuilder.Setup(x => x.From(It.IsAny<CustomerToCreateDto>())).Returns(() => null);

            var customerService = new CustomerService(_mockCustomerRepository.Object, _mockCustomerAddressBuilder.Object);

            //Act
            customerService.Create(customerToCreateDto);
        }

        [TestMethod]
        public void the_customer_shouldbesavediftheaddress_was_created()
        {
            //Arrange
            var customerToCreateDto = new CustomerToCreateDto { FirstName = "Ashok", LastName = "Samrat", City = "Magadh" };
            var _mockCustomerRepository = new Mock<ICustomerRepository>();
            var _mockCustomerAddressBuilder = new Mock<ICustomerAddressBuilder>();

            _mockCustomerAddressBuilder.Setup(x => x.From(It.IsAny<CustomerToCreateDto>())).Returns(() => "Address is Magadh");

            var customerService = new CustomerService(_mockCustomerRepository.Object, _mockCustomerAddressBuilder.Object);

            //Act
            customerService.Create(customerToCreateDto);


            //Assert
            _mockCustomerRepository.Verify(y => y.Save(It.IsAny<Customer>()));
        }

In case if the parameter is specified as out. just use out when you create a mock return.

Multiple return values


using System;
using System.Collections.Generic;

namespace Customer
{
    public class CustomerService
    {
        private readonly ICustomerRepository _customerRepository;
        private readonly IIdFactory _idFactory;

        public CustomerService(ICustomerRepository customerRepository, IIdFactory idFactory)
        {
            _customerRepository = customerRepository;
            _idFactory = idFactory;
        }

        public void Create(IEnumerable<CustomerToCreateDto> customersToCreateDto)
        {
            foreach (var customerToCreateDto in customersToCreateDto)
            {
                var customer = new Customer(customerToCreateDto.FirstName, customerToCreateDto.LastName);
                customer.Id = _idFactory.Create();

                _customerRepository.Save(customer);
            }
        }

        
    }

    public class InvalidCustomerMailingAddressException : Exception
    {
        public InvalidCustomerMailingAddressException() : base("Invalid Address")
        {
        }
    }


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

    public interface IIdFactory
    {
        int Create();
    }


    public class CustomerToCreateDto
    {   
        public String City { get; set; }
        public String FirstName { get; set; }
        public String LastName { get; set; }
        public String Name { get { return FirstName + " " + LastName; } }
    }

    public class Customer
    {
        public Customer(String firstName, String lastName)
        {
            Name = firstName + " " + lastName;
        }

        public String Name { get; set; }
        public String City { get; set; }
        public String MailingAddress { get; set; }
        public int Id { get; set; }
    }

    public class CustomerRepository : ICustomerRepository
    {
        List<Customer> customers;
        public CustomerRepository()
        {
            customers = new List<Customer>();
        }
        public void Save(Customer customer)
        {
            customers.Add(customer);
        }
    }

    public class IdFactory : IIdFactory
    {
        private int min=-1;
        private IdFactory()
        {
            min = 1;
        }
        public int Create()
        {
            min++;
            return min;
        }
    }
}

The Test


using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Collections.Generic;

namespace Customer.UnitTests
{
    [TestClass]
    public class CustomerTests
    {
        [TestMethod]
        public void each_customer_should_be_assigned_an_id()
        {
            //Arrange
            var listOfCustomersToCreate = new List<CustomerToCreateDto>
            {
                new CustomerToCreateDto(),
                new CustomerToCreateDto()
            };


            var _mockCustomerRepository = new Mock<ICustomerRepository>();
            var _mockIdFactory = new Mock<IIdFactory>();

            var i = 1;
            _mockIdFactory.Setup(x => x.Create()).Returns(() => i).Callback(() => i++);


            var customerService = new CustomerService(_mockCustomerRepository.Object, _mockIdFactory.Object);

            //Act
            customerService.Create(listOfCustomersToCreate); 

            //Assert
            _mockIdFactory.Verify(x => x.Create(), Times.Exactly(listOfCustomersToCreate.Count));
            Assert.IsTrue(i == listOfCustomersToCreate.Count + 2);
            
        }


    }


}

Arguments
  • Verifying what value was passed
  • Different behaviors for different method parameters
  • Can be used to help control SUT execution flow


using System;
using System.Collections.Generic;

namespace Customer
{
    public class CustomerService
    {
        private readonly ICustomerRepository _customerRepository;
        private readonly ICustomerFullNameBuilder _customerFullName;

        public CustomerService(ICustomerRepository customerRepository, ICustomerFullNameBuilder customerFullName)
        {
            _customerRepository = customerRepository;
            _customerFullName = customerFullName;
        }

        public void Create(CustomerToCreateDto customerToCreateDto)
        {
            var fullName = _customerFullName.From(customerToCreateDto.FirstName, customerToCreateDto.LastName);

            var customer = new Customer(fullName);

            _customerRepository.Save(customer);
        }

        
    }

    public class InvalidCustomerMailingAddressException : Exception
    {
        public InvalidCustomerMailingAddressException() : base("Invalid Address")
        {
        }
    }


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

    public interface ICustomerFullNameBuilder
    {
        String From(String firstName, String LastName);
    }


    public class CustomerToCreateDto
    {   
        public String City { get; set; }
        public String FirstName { get; set; }
        public String LastName { get; set; }
        public String Name { get { return FirstName + " " + LastName; } }
    }

    public class Customer
    {
        private string fullName;

        public Customer(String firstName, String lastName)
        {
            Name = firstName + " " + lastName;
        }

        public Customer(string fullName)
        {
            // TODO: Complete member initialization
            this.fullName = fullName;
        }

        public String Name { get; set; }
        public String City { get; set; }
        public String MailingAddress { get; set; }
        public int Id { get; set; }
    }

    public class CustomerRepository : ICustomerRepository
    {
        List<Customer> customers;
        public CustomerRepository()
        {
            customers = new List<Customer>();
        }
        public void Save(Customer customer)
        {
            customers.Add(customer);
        }
    }

    public class CustomerFullNameBuilder : ICustomerFullNameBuilder
    {
        public string From(string firstName, string LastName)
        {
            return firstName + " " + LastName;
        }
    }

}

The Test


using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Collections.Generic;

namespace Customer.UnitTests
{
    [TestClass]
    public class CustomerTests
    {
       [TestMethod]
        public void a_full_name_should_be_created_from_first_and_last_name()
        {
           //Arrange
           var customerToCreateDto = new CustomerToCreateDto{FirstName="bob", LastName="builder"};
            var _mockRepository = new Mock<ICustomerRepository>();
            var _mockCustomerFullNameBuilder = new Mock<ICustomerFullNameBuilder>();

           _mockCustomerFullNameBuilder.Setup(x=>x.From(It.IsAny<string>(), It.IsAny<string>()));
            var customerService = new CustomerService(_mockRepository.Object, _mockCustomerFullNameBuilder.Object);

           //Act
           customerService.Create(customerToCreateDto);

           //Assert
           _mockCustomerFullNameBuilder.Verify(x=>x.From(
               It.Is<string>(
                fn=>fn.Equals(customerToCreateDto.FirstName,System.StringComparison.InvariantCultureIgnoreCase)
               ),
               It.Is<string>(fn=>fn.Equals(customerToCreateDto.LastName,System.StringComparison.InvariantCultureIgnoreCase)
               )
               ));
        }
    }
}

Testing control flow


using System;
using System.Collections.Generic;

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

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

        public void Create(CustomerToCreateDto customerToCreateDto)
        {
            var customer = new Customer(customerToCreateDto.FirstName, customerToCreateDto.LastName);
            customer.StatusLevel = _customerStatusFactory.CreateFrom(customerToCreateDto);
            if (customer.StatusLevel == "Platinum")
            {
                _customerRepository.SaveSpecial(customer);
            }
            else
            {
                _customerRepository.Save(customer);
            }
        }

        
    }

    public class InvalidCustomerMailingAddressException : Exception
    {
        public InvalidCustomerMailingAddressException() : base("Invalid Address")
        {
        }
    }


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

    public interface ICustomerStatusFactory
    {
        String CreateFrom(CustomerToCreateDto customerToCreateDto);
    }


    public class CustomerToCreateDto
    {   
        public String City { get; set; }
        public String FirstName { get; set; }
        public String LastName { get; set; }
        public String Name { get { return FirstName + " " + LastName; } }
        public String StatusLevel { get; set; }
    }

    public class Customer
    {
        private string fullName;

        public Customer(String firstName, String lastName)
        {
            Name = firstName + " " + lastName;
        }

        public Customer(string fullName)
        {
            // TODO: Complete member initialization
            this.fullName = fullName;
        }

        public String Name { get; set; }
        public String City { get; set; }
        public String MailingAddress { get; set; }
        public int Id { get; set; }

        public string StatusLevel { get; set; }
    }

    public class CustomerRepository : ICustomerRepository
    {
        List<Customer> customers;
        public CustomerRepository()
        {
            customers = new List<Customer>();
        }
        public void Save(Customer customer)
        {
            customers.Add(customer);
        }


        public void SaveSpecial(Customer customer)
        {
            customers.Add(customer);
        }
    }

    public class CustomerStatusFactory : ICustomerStatusFactory
    {
        public string CreateFrom(CustomerToCreateDto customerToCreateDto)
        {
            return customerToCreateDto.StatusLevel;
        }
    }


}


[TestMethod]
        public void argument_and_execution_flow()
        {
           //Arrange
            
           var customerToCreate = new CustomerToCreateDto
            {
                //StatusLevel = "Platinum",
                StatusLevel = "Gold",
                FirstName = "Bob",
                LastName = "Builder"
            };
           var _mockCustomerRepository = new Mock<ICustomerRepository>();
            var _mockCustomerStatusFactory = new Mock<ICustomerStatusFactory>();

            _mockCustomerStatusFactory.Setup(x => x.CreateFrom(It.Is<CustomerToCreateDto>(y=>y.StatusLevel=="Platinum"))).Returns("Platinum");

            
            var customerService = new CustomerService(_mockCustomerRepository.Object, _mockCustomerStatusFactory.Object);

           //Act
            customerService.Create(customerToCreate);

           //Assert
            //_mockCustomerRepository.Verify(x => x.SaveSpecial(It.IsAny<Customer>()));
            _mockCustomerRepository.Verify(x => x.Save(It.IsAny<Customer>()));
        }

Exceptions
  • Mock object throws when invoked
  • Verifying SUT exception handling


using System;
using System.Collections.Generic;

namespace Customer
{
    public class CustomerService
    {
        private readonly ICustomerRepository _customerRepository;
        private readonly ICustomerAddressFactory _customerAddressFactory;

        public CustomerService(ICustomerRepository customerRepository, ICustomerAddressFactory customerAddressFactory)
        {
            _customerRepository = customerRepository;
            _customerAddressFactory = customerAddressFactory;
        }

        public void Create(CustomerToCreateDto customerToCreateDto)
        {
            try
            {
                var customer = new Customer(customerToCreateDto.Name);
                customer.MailingAddress = _customerAddressFactory.From(customerToCreateDto);
                _customerRepository.Save(customer);
            }
            catch (InvalidCustomerAddressException e)
            {

                throw new CustomerCreationException(e);
            }
        }
    }

    public class InvalidCustomerAddressException : Exception
    {
        public InvalidCustomerAddressException()
            : base("Invalid Address")
        {
        }
    }

    public class CustomerCreationException : Exception
    {
        private InvalidCustomerAddressException e;

        public CustomerCreationException()
            : base("Invalid Customer")
        {

        }

        public CustomerCreationException(InvalidCustomerAddressException e)
        {
            // TODO: Complete member initialization
            this.e = e;
        }
    }


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

    public interface ICustomerAddressFactory
    {
        string From(CustomerToCreateDto customerToCreateDto);
    }


    public class CustomerToCreateDto
    {
        public String City { get; set; }
        public String FirstName { get; set; }
        public String LastName { get; set; }
        public String Name { get { return FirstName + " " + LastName; } }
        public String StatusLevel { get; set; }
    }

    public class Customer
    {
        private string fullName;

        public Customer(String firstName, String lastName)
        {
            Name = firstName + " " + lastName;
        }

        public Customer(string fullName)
        {
            // TODO: Complete member initialization
            this.fullName = fullName;
        }

        public String Name { get; set; }
        public String City { get; set; }
        public String MailingAddress { get; set; }
        public int Id { get; set; }

        public string StatusLevel { get; set; }
    }

    public class CustomerRepository : ICustomerRepository
    {
        List<Customer> customers;
        public CustomerRepository()
        {
            customers = new List<Customer>();
        }
        public void Save(Customer customer)
        {
            customers.Add(customer);
        }


        public void SaveSpecial(Customer customer)
        {
            customers.Add(customer);
        }
    }
}

The test


[TestMethod]
[ExpectedException(typeof(CustomerCreationException))]
public void when_creating_a_customer_which_has_an_invalid_address()
{
    //Arrange
    var _mockRepository = new Mock<ICustomerRepository>();
    var _mockCustomerAddressFactory = new Mock<ICustomerAddressFactory>();

    _mockCustomerAddressFactory
        .Setup(x => x.From(It.IsAny<CustomerToCreateDto>()))
        .Throws<InvalidCustomerAddressException>();


    var customerService = new CustomerService(_mockRepository.Object, _mockCustomerAddressFactory.Object);

    //Act
    customerService.Create(new CustomerToCreateDto());
           
    //Assert
}

Properties
  • Verify "setter" calls
  • Return values from "getter" calls
  • Auto-mocking hierarchies(recursive mocks)

Setter


using System;
using System.Collections.Generic;

namespace Customer
{
    public class CustomerService
    {
        private readonly ICustomerRepository _customerRepository;

        public CustomerService(ICustomerRepository customerRepository)
        {
            _customerRepository = customerRepository;
        }

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

            _customerRepository.LocalTimeZone = TimeZone.CurrentTimeZone.StandardName;

            _customerRepository.Save(customer);
        }
    }

    public class InvalidCustomerAddressException : Exception
    {
        public InvalidCustomerAddressException()
            : base("Invalid Address")
        {
        }
    }

    public class CustomerCreationException : Exception
    {
        private InvalidCustomerAddressException e;

        public CustomerCreationException()
            : base("Invalid Customer")
        {

        }

        public CustomerCreationException(InvalidCustomerAddressException e)
        {
            // TODO: Complete member initialization
            this.e = e;
        }
    }


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

        string LocalTimeZone { get; set; }
    }

    public interface ICustomerAddressFactory
    {
        string From(CustomerToCreateDto customerToCreateDto);
    }


    public class CustomerToCreateDto
    {
        public String City { get; set; }
        public String FirstName { get; set; }
        public String LastName { get; set; }
        public String Name { get { return FirstName + " " + LastName; } }
        public String StatusLevel { get; set; }
    }

    public class Customer
    {
        private string fullName;

        public Customer(String firstName, String lastName)
        {
            Name = firstName + " " + lastName;
        }

        public Customer(string fullName)
        {
            // TODO: Complete member initialization
            this.fullName = fullName;
        }

        public String Name { get; set; }
        public String City { get; set; }
        public String MailingAddress { get; set; }
        public int Id { get; set; }

        public string StatusLevel { get; set; }
    }

    public class CustomerRepository : ICustomerRepository
    {
        List<Customer> customers;
        public CustomerRepository()
        {
            customers = new List<Customer>();
        }
        public void Save(Customer customer)
        {
            customers.Add(customer);
        }


        public void SaveSpecial(Customer customer)
        {
            customers.Add(customer);
        }


        public string LocalTimeZone
        {
            get;
            set;
        }
    }




}

The Test


[TestMethod]
public void the_local_timezone_should_be_set()
{
    //Arrange
    var mockCustomerRepository = new Mock<ICustomerRepository>();

    var customerService = new CustomerService(mockCustomerRepository.Object);

    //Act
    customerService.Create(new CustomerToCreateDto());

    //Assert
    mockCustomerRepository.VerifySet(x => x.LocalTimeZone = It.IsAny<string>());
}

Getter


using System;
using System.Collections.Generic;

namespace Customer
{
    public class CustomerService
    {
        private readonly ICustomerRepository _customerRepository;
        private readonly IApplicationSettings _applicationSettings;

        public CustomerService(ICustomerRepository customerRepository, IApplicationSettings applicationSettings)
        {
            _customerRepository = customerRepository;
            _applicationSettings = applicationSettings;
        }

        public void Create(CustomerToCreateDto customerToCreateDto)
        {
            var customer = new Customer(customerToCreateDto.Name);
            var workstationId = _applicationSettings.WorkstationId;

            if (!workstationId.HasValue)
            {
                throw new InvalidWorkstationIdException();
            }

            customer.WorkStationCreatedOn = workstationId.Value;
            _customerRepository.Save(customer);
        }
    }

    public class InvalidWorkstationIdException : Exception
    {
        public InvalidWorkstationIdException():base("Invalid workstationid")
        {

        }
    }

    


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

        string LocalTimeZone { get; set; }
    }

    public interface IApplicationSettings
    {
        int? WorkstationId { get; set; }
    }


    public class CustomerToCreateDto
    {
        public String City { get; set; }
        public String FirstName { get; set; }
        public String LastName { get; set; }
        public String Name { get { return FirstName + " " + LastName; } }
        public String StatusLevel { get; set; }
    }

    public class Customer
    {
        private string fullName;

        public Customer(String firstName, String lastName)
        {
            Name = firstName + " " + lastName;
        }

        public Customer(string fullName)
        {
            // TODO: Complete member initialization
            this.fullName = fullName;
        }

        public String Name { get; set; }
        public String City { get; set; }
        public String MailingAddress { get; set; }
        public int Id { get; set; }

        public string StatusLevel { get; set; }

        public int WorkStationCreatedOn { get; set; }
    }

    public class CustomerRepository : ICustomerRepository
    {
        List<Customer> customers;
        public CustomerRepository()
        {
            customers = new List<Customer>();
        }
        public void Save(Customer customer)
        {
            customers.Add(customer);
        }


        public void SaveSpecial(Customer customer)
        {
            customers.Add(customer);
        }


        public string LocalTimeZone
        {
            get;
            set;
        }
    }




}

The test


[TestMethod]
        public void the_workstation_id_should_be_used()
        {
           //Arrange
            var mockCustomerRepository = new Mock<ICustomerRepository>();
            var mockApplicationSettings = new Mock<IApplicationSettings>();

            mockApplicationSettings
                .Setup(x => x.WorkstationId)
                .Returns(123);


            var customerService = new CustomerService(mockCustomerRepository.Object, mockApplicationSettings.Object);

           //Act
            customerService.Create(new CustomerToCreateDto());

           //Assert
            mockApplicationSettings.VerifyGet(x => x.WorkstationId);
        }

Property Hierarchy

Stubbing Properties

  • Pre-set values for properties on mock objects
  • Changing those values
  • SetupAllProperties

Events
  • Raise on the mock
  • Raise further down the hierarchy
  • Non-standard event signatures

Advanced Mocking

Outline

  • Strict and Loose Mocking
  • Base Class Implementations
  • Recursive Mocking
  • Centralized Mock Creation
  • Protected Members

Strict vs Loose Mocking
  • Strict
    • raises exception for anything on a mock object that doesn't have an explicitly declared expectation
  • Loose
    • no exception raised
    • returns default values when no expectation is explicitly declared
  • Loose is the Moq default behavior


using System;
using System.Collections.Generic;

namespace Customer
{
    public class CustomerService
    {
        private readonly ICustomerRepository _customerRepository;
        public CustomerService(ICustomerRepository customerRepository)
        {
            _customerRepository = customerRepository;
        }

        public void Create(CustomerToCreateDto customerToCreate)
        {
            var customer = new Customer(customerToCreate.Name);

            _customerRepository.Save(customer);
            _customerRepository.FetchAll();
        }
    }

    public interface ICustomerRepository
    {
        void FetchAll();

        void Save(Customer customer);
    }

    public class CustomerToCreateDto
    {
        public object Name { get; set; }
    }

    public class Customer
    {
        private object p;

        public Customer(object p)
        {
            // TODO: Complete member initialization
            this.p = p;
        }

    }
}

The Test


[TestMethod]
public void the_customer_should_be_saved()
{
    //Arrange
    //Loose Mock
    //var mockCustomerRepository = 
        //new Mock<ICustomerRepository>();

    //Strict Mock
    var mockCustomerRepository = 
        new Mock<ICustomerRepository>(MockBehavior.Strict);

            
    mockCustomerRepository
        .Setup(x => x.Save(It.IsAny<Customer>()));

    var customerService = new CustomerService(mockCustomerRepository.Object);

    //Act
    customerService.Create(new CustomerToCreateDto());

    //Assert
    mockCustomerRepository.Verify();
            
}

Base Class Implementations
  • aka "Partial Mocks"
  • Allows for invocation of base class implementation if no expectation has been set on the member
  • Necessary for mocking Web/Html controls in System.Web


using System;

namespace Customer
{
    public class CustomerNameFormatter: BaseFormatter
    {
        public string From(Customer customer)
        {
            var firstName = ParseBadWordsFrom(customer.FirstName);
            var lastName = ParseBadWordsFrom(customer.LastName);

            return String.Format("{0}, {1}", lastName, firstName);
        }
    }

    public abstract class BaseFormatter
    {
        public virtual string ParseBadWordsFrom(string value)
        {
            return value.Replace("SAP", string.Empty);
        }
    }
}

The Test


[TestMethod]
        public void bad_words_should_be_stripped_from_the_first_and_last_names()
        {
            //Arrange
            var mockNameFormatter = new Mock<CustomerNameFormatter>();

            //Act
            mockNameFormatter.Object.From(new Customer("Bob", "SAPBuilder"));

            //Assert
            mockNameFormatter.Verify(
                x=>x.ParseBadWordsFrom(It.IsAny<string>()), Times.AtLeast(2)
                );
        }

Recursive Mocking
  • A mock object that returns a new mock object for every member
    • If the member is mockable (i.e. not a value type)
    • If there is no expectation set on the member
  • The same mock object is returned on all calls

Centralizing Mock Creation
  • MockRepository
  • Configure all mock objects centrally
  • Verify all mock objects in one call


using System;
using System.Collections.Generic;

namespace Customer
{
    public class CustomerService
    {
        private readonly ICustomerRepository _customerRepository;
        private readonly ICustomerAddressFormatter _customerAddressFormatter;
        public CustomerService(ICustomerRepository customerRepository, ICustomerAddressFormatter customerAddressFormatter)
        {
            _customerRepository = customerRepository;
            _customerAddressFormatter = customerAddressFormatter;
        }

        public void Create(CustomerToCreateDto customerToCreate)
        {
            var customer = new Customer(customerToCreate.Name);
            customer.Address = _customerAddressFormatter.For(customerToCreate);

            _customerRepository.FetchAll();
        }
    }

    public interface ICustomerRepository
    {
        void FetchAll();

        void Save(Customer customer);
    }

    public interface ICustomerAddressFormatter
    {
        string For(CustomerToCreateDto customerToCreate);
    }

    public class CustomerToCreateDto
    {
        public object Name { get; set; }
    }

    public class Customer
    {
        private object p;
        private string p1;
        private string p2;

        public Customer(object p)
        {
            // TODO: Complete member initialization
            this.p = p;
        }

        public Customer(string p1, string p2)
        {
            // TODO: Complete member initialization
            this.p1 = p1;
            this.p2 = p2;
        }


        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string Address { get; set; }
    }
}

The test


[TestMethod]
public void the_address_should_be_formatted()
{
//Arrange
var mockFactory = new MockRepository(MockBehavior.Loose) { DefaultValue = DefaultValue.Mock };

var mockCustomerRepository = mockFactory.Create<ICustomerRepository>();
var mockCustomerAddressFormatter = mockFactory.Create<ICustomerAddressFormatter>();

mockCustomerAddressFormatter
    .Setup(x => x.For(It.IsAny<CustomerToCreateDto>()))
    .Returns("new address");

var customerService = new CustomerService(
    mockCustomerRepository.Object, mockCustomerAddressFormatter.Object
    );

//Act
customerService.Create(new CustomerToCreateDto());

//Assert
mockFactory.Verify();
}

Protected Members
  • Mocking members that use the "protected" keyword
  • No intellisense
  • Must use "ItExpr" instead of "It"