Unit Testing with Moq

Introduction

There are a lot of articles on how to use Moq, but I’m going to bring out my die roller game example to show how to use Moq to roll a sequence of predetermined results.  I’m also going to do this using .Net Core.

The Setup

My sample program is a game.  The game is actually empty, because I want to show the minimal code to demonstrate Moq itself.  So let’s pretend there is a game object and it uses a die roll object to get a random outcome.  For those who have never programmed a game before, a die roll can be used to determine offense or defense of one battle unit attacking another in a turn-based board game.  However, unit tests must be repeatable and we must make sure we test as much code as possible (maximize our code coverage).

The sample project uses a Game object that is dependent on the DieRoller object.  To break dependencies, I required an instance of the DieRoller object to be fed into the Game object’s constructor:

public class Game
{
    private IDieRoller _dieRoller;

    public Game(IDieRoller dieRoller)
    {
        _dieRoller = dieRoller;
    }

    public int Play()
    {
        return _dieRoller.DieRoll();
    }
}

Now I can feed a Moq object into the Game object and control what the die roll will be.  For the game itself, I can use the actual DieRoller object by default:

public static void Main(string[] args)
{
    var game = new Game(new DieRoller());
}

An IOC container could be used as well, and I would highly recommend it for a real project.  I’ll skip the IOC container for this blog post.

The unit test can look something like this:

[Fact]
public void test_one_die_roll()
{
	var dieRoller = new Mock();
	dieRoller.Setup(x => x.DieRoll())
	.Returns(2);

	var game = new Game(dieRoller.Object);
	var result = game.Play();
	Assert.Equal(2, result);
}

I’m using xunit and moq in the above example.  So for my .Net Core project.json file:

{
	"version": "1.0.0-*",
	"testRunner": "xunit",
	"dependencies": {
		"DieRollerLibrary": "1.0.0-*",
		"GameLibrary": "1.0.0-*",
		"Microsoft.NETCore.App": {
			"type": "platform",
			"version": "1.0.1"
	},
	"Moq": "4.6.38-alpha",
	"xunit": "2.2.0-beta2-build3300",
	"xunit.core": "2.2.0-beta2-build3300",
	"dotnet-test-xunit": "2.2.0-preview2-build1029",
	"xunit.runner.visualstudio": "2.2.0-beta2-build1149"
},

"frameworks": {
	"netcoreapp1.0": {
		"imports": "dnxcore50"
		}
	}
}

Make sure you check the versions of these packages since they are constantly changing as of this blog post.  It’s probably best to use the NuGet package window or the console to get the latest version.

Breaking Dependencies

What does Moq do?  Moq is a quick and dirty way to create a fake object instance without writing a fake object.  Moq can take an interface or object definition and create a local instance with outputs that you can control.  In the XUnit sample above, Moq is told to return the number 2 when the DieRoll() method is called.  

Why mock an object?  As you create code, you’ll end up with objects that call other objects.  These cause dependencies.  In this example, the Game object is dependent on the DieRoller object:

Each object should have it’s own unit tests.  If we are testing two or more objects that are connected together, then technically, we’re performing an integration test.  To break dependencies, we need all objects not under test to be faked or mocked out.  If the Game object has multiple paths (using if/then, case statements for example) that depend on the roll of the die, then we’ll need to create unit tests where we can fix the die roll to a known set of values and execute the Game object to see the expected results.

First, I’m going to add a method to the Game class that will determine the outcome of an attack.  If the die roll is greater than 4, then the attack is successful (unit is hit).  If the die roll is 4 or less, then it’s a miss.  I’ll use true for a hit and false for a miss.  Here is my new Game class:

public class Game
{
    private IDieRoller _dieRoller;

	public Game(IDieRoller dieRoller)
	{
		_dieRoller = dieRoller;
	}

	public int Play()
	{
		return _dieRoller.DieRoll();
	}
	 
	public bool Attack()
	{
		if (_dieRoller.DieRoll() > 4)
		{
			return true;
		}
		
		return false;
	}
}

Now if we define a unit test like this:

[Theory]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
[InlineData(4)]
public void test_attack_unsuccessful(int dieResult)
{
	var dieRoller = new Mock();
	dieRoller.Setup(x => x.DieRoll())
	.Returns(dieResult);

	var game = new Game(dieRoller.Object);
	var result = game.Attack();
	Assert.False(result);
}

We can test all instances where the die roll should produce a false result.  To make sure we have full coverage, we’ll need to test the other two die results (where the die is a 5 or a 6):

[Theory]
[InlineData(5)]
[InlineData(6)]
public void test_attack_successful(int dieResult)
{
	var dieRoller = new Mock();
	dieRoller.Setup(x => x.DieRoll())
	.Returns(dieResult);

	var game = new Game(dieRoller.Object);
	var result = game.Attack();
	Assert.True(result);
}

Another Example

Now I’m going to make it complicated.  Sometimes in board games, we use two die rolls to determine an outcome.  First, I’m going to define an enum to allow three distinct results of an attack:

public enum AttackResult
{
	Miss,
	Destroyed,
	Damaged
}

Next, I’m going to create a new method named Attack2():

public AttackResult Attack2()
{
	if (_dieRoller.DieRoll() > 4)
	{
		if (_dieRoller.DieRoll() > 3)
		{
			return AttackResult.Damaged;
		}
		return AttackResult.Destroyed;
	}
	return AttackResult.Miss;
}

As you can see, the die could be rolled up to two times.  So, in order to test your results, you’ll need to fake two rolls before calling the game object.   I’m going to use the “theory” XUnit attribute to feed values that represent a damaged unit.  The values need to be the following:

5,4
5,5
5,6
6,4
6,5
6,6 

Moq has a SetupSequence() method that allows us to stack predetermined results to return.  So every time the mock object is called, the next value will be returned.  Here’s the XUnit test to handle all die rolls that would result with an AttackReuslt of damaged:

[Theory]
[InlineData(5, 4)]
[InlineData(5, 5)]
[InlineData(5, 6)]
[InlineData(6, 4)]
[InlineData(6, 5)]
[InlineData(6, 6)]
public void test_attack_damaged(int dieResult1, int dieResult2)
{
	var dieRoller = new Mock();
	dieRoller.SetupSequence(x => x.DieRoll())
	.Returns(dieResult1)
	.Returns(dieResult2);

	var game = new Game(dieRoller.Object);
	var result = game.Attack2();
	Assert.Equal(AttackResult.Damaged, result);
}

Next, the unit testing for instances where the Attack2() method returns a AttackResult  of destroyed:

[Theory]
[InlineData(5, 1)]
[InlineData(5, 2)]
[InlineData(5, 3)]
[InlineData(6, 1)]
[InlineData(6, 2)]
[InlineData(6, 2)]
public void test_attack_destroyed(int dieResult1, int dieResult2)
{
	var dieRoller = new Mock();
	dieRoller.SetupSequence(x => x.DieRoll())
	.Returns(dieResult1)
	.Returns(dieResult2);

	var game = new Game(dieRoller.Object);
	var result = game.Attack2();
	Assert.Equal(AttackResult.Destroyed, result);
}

And finally, the instances where the AttackResult is a miss:

[Theory]
[InlineData(1, 1)]
[InlineData(2, 2)]
[InlineData(3, 3)]
[InlineData(4, 1)]
public void test_attack_miss(int dieResult1, int dieResult2)
{
	var dieRoller = new Mock();
	dieRoller.SetupSequence(x => x.DieRoll())
	.Returns(dieResult1)
	.Returns(dieResult2);

	var game = new Game(dieRoller.Object);
	var result = game.Attack2();
	Assert.Equal(AttackResult.Miss, result);
}

In the instance of the miss, the second die roll doesn’t really matter and technically, the unit test could be cut back to one input.  To test for every possible case, we could feed all six values into the second die.  Why would be do that?  Unit tests are performed for more than one reason.  Initially, they are created to prove our code as we write it.  Test-driven development is centered around this concept.  However, we also have to recognize that after the code is completed and deployed, the unit tests become regression tests.  These tests should live with the code for the life of the code.  The tests should also be incorporated into your continuous integration environment and executed every time code is checked into your version control system (technically, you should execute the tests every time you build, but your build times might be too long to do this).  This will prevent future code changes from accidentally breaking code that was already developed and tested.  In the Attack2() method a developer could enhance the code to use the second die roll when the first die roll is a 1,2,3 or 4.  The unit test above will not necessarily catch this change.  The only thing worse than a broken unit test is one that passes when it shouldn’t.

With that said, you should not have to perform an exhaustive test on every piece of code in your program.  I would only recommend such a tactic if the input data set was small enough to be reasonable.  For the example case above, the die size is 6 and the “Theory” attribute cuts the code you’ll need in order to perform multiple unit tests.  If you are using Microsoft Tests, then you can setup a loop that does the same function as the “Theory” attribute and test all iterations for one expected output in each unit test.

Where to get the Sample Code

You can download the sample code from my GitHub account by clicking here.

Leave a Reply