Allow me to mock your code

That's not to say I'm going to be taking the proverbial out of your creation, although I might....

One of the big parts of unit testing is being able to mock and fake dependencies. Doing this we can create a controlled environment for the code under test. Before we dive into examples, let's get the boring paperwork out of the way.

What is a Mock?

According to the good old dictionary, one meaning is:

to attack or treat with ridicule, contempt, or derision.

Now, I'm no wordsmith but that doesn't seem useful to software development. I'll go with this particular meaning:

to mimic, imitate, or counterfeit. 

With mocking we create a 'counterfeit' version of a dependency that we can inject in to the code under test. We don't need a concrete implementation to do this. It allows us to control the dependency behavior and re-create the exact situation we are trying to test.

Because we don't need a concrete class, we can just use the interface of the dependency to create the mocked version.

What is a Fake?

A fake is when we create a concrete implementation of the dependency used just for testing. This is useful when you want to do something functional on top of a simple mock. Although some of the time if you have to use a fake you may have got your abstraction wrong.

However, there are still certain cases where it will be easier to create a fake.

Examples

We have a dependency that creates a customer, then returns the Id of that new customer. I'm using integers for id's and not Guid's. I'm not an animal...

I'll refer you to one of those database guys if you want to read more about why not to use Guids as primary keys...

Our Interface looks like this:

public interface ICustomerCreator
{
	int Create(string emailAddress);
}

We could go on to create a fake for this, and maybe pass the expected output in to the constructor, which would give us:

public class FakeCustomerCreator : ICustomerCreator
{
	private readonly int _expectedOutput;

	public FakeCustomerCreator(int expectedOutput)
	{
		_expectedOutput = expectedOutput;
	}
	public int Create(string emailAddress)
	{
		return _expectedOutput;
	}
}

Alternatively we could create a mock using something like NSubstitute:

var expected = 123;
var customerCreator = Substitute.For();
customerCreator.Create(Arg.Any()).Returns(expected);

Notice here that I am specifying that the email address parameter can be any string at all. You can specify actual values in there is you want to check against a particular value.

How is this useful?

So far we have created code that does nothing. In the context of unit testing, we have actually created code that we can control.

Let's imagine that this code returns 0 when the customer is a duplicate. I know, I know... this is bad. I should return a class with whether it was successful or not... I'm just trying to keep things simple.

It is consumed in a piece of code which creates a customer and sends a welcome email if the customer is created, and this is what we want to test.

Lets create the code under test:

public interface ICustomerService
{
	bool CreateAndSendWelcomeEmail(string emailAddress);
}

public interface IEmailService
{
	void SendWelcomeEmail(string emailAddress);
}

public class CustomerService : ICustomerService
{
	private readonly ICustomerCreator _customerCreator;
	private readonly IEmailService _emailService;

	public CustomerService(ICustomerCreator customerCreator, IEmailService emailService)
	{
		_customerCreator = customerCreator;
		_emailService = emailService;
	}
	public bool CreateAndSendWelcomeEmail(string emailAddress)
	{
		var id = _customerCreator.Create(emailAddress);
		if (id ==0) return false;
		_emailService.SendWelcomeEmail(emailAddress);
		return true;
	}
}

I have taken the liberty of creating the interface for the email service, hope you don't mind. Please note that I haven't created the email service. I don't need to worry about the implementation of that yet.

In a production environment you probably wouldn't couple these together. I'm sure in future posts I'll explore service buses, events and such the like.

This is the code we will be testing. We want to check the following conditions:

  1. If a customer is created, it sends the welcome email
  2. If a customer isn't created, it doesn't send the welcome email

Our test will look something like this:

[TestFixture]
public class CustomerCreationTests
{
	public static IEnumerable CustomerData
	{
		get
		{
			//Don't send the email if they aren't created
			yield return new object[] { 0, false };
			//Do send the email if they are created
			yield return new object[] { 123, true };
		}
	}

	[Test]
	[TestCaseSource(nameof(CustomerData))]
	public void CreateAndSendEmailTest(int returnId, bool emailSent)
	{
		//arrange
		var customerCreator = Substitute.For();
		customerCreator.Create(Arg.Any()).Returns(returnId);
		var emailService = Substitute.For();
		var customerService = new CustomerService(customerCreator, emailService);

		//act
		customerService.CreateAndSendWelcomeEmail("[email protected]");

		//assert
		emailService.Received(emailSent ? 1 : 0).SendWelcomeEmail("[email protected]");
	}
}

In the assert line, we are checking if the 'SendWelcomeEmail' method has been called a given number of times.

If we run that test, we should see the satisfaction of two green ticks as a measure of just how ace we are.

We aren't checking that the method itself returns correctly however. Lets create that test then:

public static IEnumerable ExistingData
{
	get
	{
		//Is Existing
		yield return new[] { false, true };
		//Not Existing
		yield return new[] { true, false };
	}
}

[Test]
[TestCaseSource(nameof(ExistingData))]
public void ReturnsCustomerCreated(bool existing, bool expected)
{
	//arrange
	var customerCreator = Substitute.For();
	customerCreator.Create(Arg.Any()).Returns(existing ? 0 : 123);
	var emailService = Substitute.For();
	var customerService = new CustomerService(customerCreator, emailService);

	//act
	var output = customerService.CreateAndSendWelcomeEmail("[email protected]");

	//assert
	output.Should().Be(expected);
}

There is another two green ticks for our collection!

To summarise where we are at, we have checked that it returns the correct response and that it will only send the email when the customer is created successfully. If we were to change any of the code under test, our tests will now fail.

This is a very useful way to build resilience in to systems as you layer dependencies on top of each other.

In the next post we will look in to Test Driven Development (TDD) in depth and when to use it.

  1. What is Unit Testing?
  2. My unit testing setup
  3. Remove statics to make code unit testable
  4. Allow me to mock your code