Unit Testing – Dependencies

Summary

I’ve written about unit testing quite extensively, but this time I want to show a method to break dependency using some code that I blogged about earlier.  The code I’m talking about is used to detect if a method or class is being executed under a unit test.  In my example I’m going to pretend I’m designing a game (because I am) and show how to test the die roll.

To get started, I’ll describe what I’m trying to design.  Over a year ago, I blogged about a game I named Battle Field One.  This game is based on an electronic version of a physical board game using army tokens and a hex board.  When one unit attacks another unit, the result of the attack is determined by die roll.  In this unit test setup, I’m going to show how you can fake your die roller for unit testing purposes.


Getting Started

Why would we want to fake out the die roll?  If we’re unit testing, we want to control what the outcome is so we can test each outcome.  For instance, we can test what the battle outcome will be depending on each die roll.  If we don’t fake the die roll, then the random number generator will generate a number that we don’t know in the unit test and the test would be difficult to perform.

Next is the problem of multiple rolls.  We don’t just want to set one number, but we might want to stack the dice with numbers for the next N rolls of the dice.  So I’m gong to design a unit test helper to do this for us.


The Solution Setup

For my solution, I created four projects: A console, the game class, the unit test helper class and the unit tests class.  I’ll start with the game project, containing the GameClass.  In this project is just one class file that is called the DieRoller:

public static class DieRoller
{
    private static Random RandomNumberGenerator = new 

       Random(DateTime.Now.Millisecond);

    public static int DieRoll()
    {
        if (UnitTestHelpers.IsInUnitTest)
        {
            return UnitTestHelpers.SetDieRoll;
        }
        else
        {
            return RandomNumberGenerator.Next() % 6;
        }
    }
}

The DieRoll() method contains a call to the unit test helper class that has a SetDieRoll getter.  The UnitTestHelper class looks like this:

public class UnitTestHelpers
{
    public static bool IsInUnitTest
    {
        get
        {
            string assemblyName = 

            “Microsoft.VisualStudio.QualityTools.UnitTestFramework“;
            return AppDomain.CurrentDomain.GetAssemblies().Any(a => 

               a.FullName.StartsWith(assemblyName));
        }
    }
    private static int _NextDieRoll = 0;
    private static List<int> _SetDieRoll = new List<int>();
    public static int SetDieRoll
    {
        get
        {
            int nextDieRoll = _SetDieRoll[_NextDieRoll];
            _NextDieRoll++;
            if (_NextDieRoll >= _SetDieRoll.Count)
            {
                _NextDieRoll = 0;
            }

            return nextDieRoll;
        }
        set
        {
            _SetDieRoll.Add(value);
        }
    }
}

The “IsInUnitTest” will detect if the starting project is a unit test project (because it contains the “UnitTestFramework” dll).  The SetDieRoll getter, setter method is used to set one or more die rolls ahead of it’s use.  The getter will grab the next roll in the list and then wrap when the list has been exhausted.


Writing a Unit Test

[TestMethod]
public void TestDieRoll()
{
    UnitTestHelpers.SetDieRoll = 5;
    UnitTestHelpers.SetDieRoll = 2;
    UnitTestHelpers.SetDieRoll = 7;

    int result = DieRoller.DieRoll();
    Assert.AreEqual(5, result);

    result = DieRoller.DieRoll();
    Assert.AreEqual(2, result);

    result = DieRoller.DieRoll();
    Assert.AreEqual(7, result);
}

First, we put three numbers in our die.  Then we call the DieRoller three times and verify that the numbers that are read are the numbers that we stuffed.  


The Console Application

Next, we need to verify that the unit test part is not interfering with the normal “random” operation of the die.  So I wrote this dumb and dirty program to manually read a few die rolls:

class Program
{
    static void Main(string[] args)
    {
        int result = DieRoller.DieRoll();
        Console.WriteLine(result);
        Console.ReadKey();
    }
}


You should get a number between 1 and 6 each time you run the console application.  If you run the program more than once, you should get a different number each time.  What you should not get is a 5 followed by a 2 followed by a 7 (it could randomly happen, but probably not).  If you run the console and it always produce 5, then there is a problem.  You’ll need to investigate the IsInUnitTest method.


Download the Sample Solution

You can download my sample application by clicking here.

Update

There is a possible issue with running multiple unit tests with the static SetDieRoll method.  When one unit tests sets up the rolls, then another test inserts new data, the previous test data is still stored.  To fix this issue you should provide a DieClear() method, like this:


public class UnitTestHelpers
{

    public static void ClearDieRoll()
    {
        _SetDieRoll.Clear();
        _NextDieRoll = 0;
    }

}

Then add an initialize method to your unit tests:

[TestInitialize]
public void Initialize()
{
    UnitTestHelpers.ClearDieRoll();
}


This will clear any die roll information between multiple tests.

 

Leave a Reply