Pseudorandom Knowledge

Unit testing: replacing dependencies

The point of unit testing is to test classes in isolation. This presents a problem when the class has dependencies. These dependencies must be replaced somehow.

namespace SpaceGame
{
    public class Level
    {
        private IEnumerable<IEnemy> enemies;
 
        public Level(params IEnemy[] enemies)
        {
            this.enemies = enemies;
        }
 
        public bool Cleared
        {
            get { return !enemies.Any(e => e.Alive); }
        }
 
        public void Shoot(int damage)
        {
            damage = damage / enemies.Count();
            foreach (var enemy in enemies)
            {
                enemy.Hit(damage);
            }
        }
    }
}

Dependency injection gives an opportunity to make those replacements. The dependencies are passed to the class instead of the class creating them itself. And by using interfaces the dependencies can be replaced with something else.

namespace SpaceGame.UnitTests
{
    [TestFixture]
    public class LevelTests
    {
        [Test]
        public void OneEnemyNotAlive_IsCleared()
        {
            var enemy = Substitute.For<IEnemy>();
            enemy.Alive.Returns(false);
 
            Level level = new Level(enemy);
 
            Assert.IsTrue(level.Cleared);
        }
 
        [Test]
        public void Shoot_TwoEnemies_DealsHalfDamageToEach()
        {
            var enemy1 = Substitute.For<IEnemy>();
            var enemy2 = Substitute.For<IEnemy>();
 
            Level level = new Level(enemy1, enemy2);
            level.Shoot(100);
 
            enemy1.Received().Hit(50);
            enemy2.Received().Hit(50);
        }
    }
}

Isolation frameworks provides a convenient way to create replacements for dependencies. The replacements can be configured to behave as needed for the test. They can also be used to check that they are called appropriately during the test. There are many isolation frameworks available for .NET. This example uses NSubstitute.