Tic-Tac-Tutorial: Mocking with Interfaces

In the previous installment, we discussed how to identify dependencies and replace them with Test Doubles in order to properly unit test them. I demonstrated one way to do this by defining vital functionality in abstract base classes, then creating special implementations for the unit tests.

It’s a straightforward and no-frills pattern, but clunky and overly verbose. Fortunately, there is more elegant way of creating test doubles: using interfaces and a good Mocking library.

Download the source code to follow along.

Converting abstract base classes to interfaces

On the surface, abstract classes and interfaces behave similarly – they allow you to define contracts that a class implements. However, they differ in principle: abstract classes define what a class is, where interfaces define what a class can do. So in many cases, using abstract base classes is probably more accurate than using interfaces. On the other hand, they differ less in practice, and when it comes to Mocking, a good library will handle either way.

But there are some good reasons to favor interfaces when it comes to structuring your codebase, identifying dependencies, and writing unit tests:

  • interfaces are more lightweight than abstracts
  • interfaces have no code, making them easier to test with
  • In .NET there is no way to do multiple inheritance, so using abstract classes can limit code re-use.

With these advantages in mind, we’ll update the TicTacTutorial codebase and replace the abstract base classes with interfaces.

Let’s review the previous implementation of RandomProvider and GameManager:

public abstract class RandomProviderBase
{
    public abstract int Get(int? maxValue = null);
}
public class RandomProvider : RandomProviderBase
{
    private readonly Random rnd;

    public RandomProvider(int? seed = null)
    { rnd = seed != null ? new Random(seed.Value) : new Random(); }

    public override int Get(int? maxValue = null)
    { return maxValue != null ? rnd.Next(maxValue.Value) : rnd.Next(); }
}
public class GameManager
{
    private RandomProviderBase myRandomProvider;

    protected RandomProviderBase RandomProvider => myRandomProvider ?? (myRandomProvider = this.MakeRandomProvider());

    protected virtual RandomProviderBase MakeRandomProvider()
    { return new RandomProvider(); }
...

The benefit of this pattern is that the production (real-world) implementation is hard-coded and you don’t have to worry about explicitly wiring up dependencies when it comes time to go live – but it is less flexible than other patterns, and thus takes more code and more effort to test. In the redesign that we’ll explore shortly, the common practice is to require dependencies in the constructor, which is good for explicitness and flexibility, but can make the entire system difficult to wire up easily. We’ll address that in a later post on Dependency Injection.

For now, let’s rewrite the RandomProvider and GameManager to expose and consume interfaces:

public interface IRandomProvider
{
    int Get(int? maxValue = null);
}
public class RandomProvider : IRandomProvider
{
    private readonly Random rnd;

    public RandomProvider(int? seed = null)
    {
        rnd = seed != null ? new Random(seed.Value) : new Random();
    }

    public int Get(int? maxValue = null)
    {
        return maxValue != null ? rnd.Next(maxValue.Value) : rnd.Next();
    }
}
public class GameManager
{
    private readonly IRandomProvider RandomProvider;

    public GameManager(IRandomProvider rand)
    {
        RandomProvider = rand;
    }
...

These changes will of course break our existing tests, so let’s update those as well.

First, we’ll get rid of GameManagerTestable.cs, since we no longer need to create a special implementation of the base class.

Next, we need to resolve all the errors related to RandomProviderBase in the RandomProvider Test Double implementations. In these cases we can simply change RandomProviderBase to IRandomProvider and remove the now-unnecessary override keyword from the method signatures.

Then we’ll update the broken tests and change references to GameManagerTestable back to GameManager, like this one in GameManagerTests.cs:

[TestMethod]
public void GameManager_CreateNew_should_return_a_new_game()
{
    // Arrange
    var gameManager = new GameManager(null);
...

With the updated GameManager constructor, an IRandomProvider instance is now required. For now, let’s just pass null to the constructor so we can get everything to compile. Also, comment-out the remaining errors in this file.

There’s a couple more files with compile errors, go ahead and resolve those by passing in null for now.

The code now compiles, so let’s run the tests to see what we broke:

Broken Tests

Unsurprisingly, these are mostly tests where we commented-out the RandomProvider Test Doubles. Let’s start passing them back into the GameManager:

[TestMethod]
public void GameManager_CreateNewRandom_with_random_0_should_not_swap_players()
{
    // Arrange
    var gameManager = new GameManager(new RandomProviderStubReturns0());
    var firstPlayer = Guid.NewGuid().ToString();
    var secondPlayer = Guid.NewGuid().ToString();
    //gameManager.MakeRandomProviderOverride = () => new RandomProviderStubReturns0();
...

When we run this test again, we can see that this change fixed the issue – so go ahead and update the other tests which use IRandomProvider Test Doubles, and run the tests to confirm they now pass.

That leaves just the final failing test:

[TestMethod]
[TestCategory("Integration")]
public void GameManager_CreateNewRandom_should_swap_players_50_pct_of_the_time()
{
    // need a large number of runs to ensure a smooth statistical distribution
    var testRuns = 10000;
    var numSwapped = 0;
    var numNotSwapped = 0;
    var player1 = Guid.NewGuid().ToString();
    var player2 = Guid.NewGuid().ToString();
    var gameManager = new GameManager(null);

This test requires the actual RandomProvider implementation (which was previously hard-coded into the GameManager), so now we’ll have to manually supply it:

[TestMethod]
[TestCategory("Integration")]
public void GameManager_CreateNewRandom_should_swap_players_50_pct_of_the_time()
{
    // need a large number of runs to ensure a smooth statistical distribution
    var testRuns = 10000;
    var numSwapped = 0;
    var numNotSwapped = 0;
    var player1 = Guid.NewGuid().ToString();
    var player2 = Guid.NewGuid().ToString();
    var gameManager = new GameManager(new RandomProvider());

Run the test and confirm that it now passes.

Are you starting to appreciate unit tests? We introduced a significant change into our codebase, which was immediately exposed by broken test code; after we fixed the compile errors, we saw the impacted tests fail; after we re-wired the tests, we confirmed our changes had no unexpected side-effects.

Red-Green-Refactor!

Mocking for fun and profit

Hey Girl

There’s one more clunky thing I want to fix: the RandomProvider Test Double implementations.

There’s a way to provide the same capability as these Test Doubles but without having additional classes, and that’s by using a Mocking library.

For this example, we’ll use Moq which provides a very elegant and Fluent-style way to create Test Doubles.

Use NuGet to add Moq to the TicTacToeCore.Tests project.

Let’s start by replacing RandomProviderStubReturns0:

[TestMethod]
public void GameManager_CreateNewRandom_with_random_0_should_not_swap_players()
{
    // Arrange
    var gameManager = new GameManager(new RandomProviderStubReturns0());
...

We can replace this separate Test Double class with a configurable object that we can define with a few lines of a code!

[TestMethod]
public void GameManager_CreateNewRandom_with_random_0_should_not_swap_players()
{
    // Arrange
    var randomMock = new Mock<IRandomProvider>(MockBehavior.Strict);
    randomMock.Setup(m => m.Get(It.IsAny<int?>())).Returns(0);
    var gameManager = new GameManager(randomMock.Object);
...

The way to read this code is:
5: Create a new Mock that implements IRandomProvider, and configure its behavior so that any call to it must be explicitly set-up or it will throw an exception
6: Setup the IRandomProvider.Get implementation so that for any int? that is passed in, return 0.
7: Pass the test double we just created into the constructor

A nice thing about using Moq (instead of testable implementations) is that you can easily see right in the test method how the Test Double is going to behave. Having this behavioral logic right in front of your face while you’re trying to read a test is extremely beneficial, as opposed to being hidden several tabs away in another file.

Run this test and confirm that it still passes, then go ahead and convert the GameManager_CreateNewRandom_with_random_1_should_swap_players test.

Then we’ll learn how to replace the Spy:

[TestMethod]
public void GameManager_CreateNewRandom_calls_RandomProvider()
{
    // Arrange
    var expectedReturnValueForRandomProviderGet = 1;
    var spy = new RandomProviderSpy(
        new RandomProviderMock { GetOverride = maxValue => expectedReturnValueForRandomProviderGet });
    var gameManager = new GameManager(spy);
...
    // Assert
    Assert.AreEqual(1, spy.GetSpy.Calls.Count, "Expected 1 call to RandomProvider.Get()");
    Assert.AreEqual(2, spy.GetSpy.Calls[0].Item1, 
        "Expected maxValue parameter of RandomProvider.Get() call #1 to be \"2\"");
    Assert.AreEqual(
        expectedReturnValueForRandomProviderGet, 
        spy.GetSpy.Calls[0].Item2, 
        $"Expected return value of RandomProvider.Get() call #1 to be \"{expectedReturnValueForRandomProviderGet}\"");
}

The setup is the same change as we’ve done before so i’ll leave that to you to figure out; but for the assertions, we’ll introduce and use a different method, Mock.Verify:

    // Assert
    randomMock.Verify(m => m.Get(2), Times.Once());
}

The way to read this code is:
11: Verify that the mocked Get method was called with the parameter 2 and called exactly once.

What used to be a couple dozen lines of Test Double code and a few lines of assertions has now been distilled down into 3 lines.

Moq makes Spying much easier and simpler.

No, scratch that: Moq makes all testing easier and simpler.

That’s all for this installment!

If you’ve found this tutorial useful, please share it! Got suggestions or feedback? Please leave a comment – thanks!

Download the finished source code here

One thought on “Tic-Tac-Tutorial: Mocking with Interfaces

  1. Pingback: Tic-Tac-Tutorial Series | PhilChuang.com

Leave a Reply