Unit Testing Part 2

In the last post, I started covering unit tests. Specifically, I talked about how only testing one unit at a time can help us with debugging. I also covered the basic usage of xUnit.

Understanding basic unit testing concepts can get you a long way, but before too long, you’ll find that you need some more advanced techniques. This post will cover mocking interfaces in order to test code that interacts with outside applications (such as databases).

When it comes to writing testable code, interfaces are your friends. Interfaces make your code cleaner and easier to test. Here is the classic barn yard example (adapted from https://spin.atomicobject.com/2014/04/29/code-untestable-part-2-developers/).

public class Pig {
    public void Oink() {
        Console.WriteLine("Oink!");
    }
}

public class Cow {
    public void Moo() {
        Console.WriteLine("Moo!");
    }
}

public class Rooster {
    public void Crow() {
        Console.WriteLine("Crow!");
    }
}

public class BarnYard {
    public void Noises() {
        var pig = new Pig();
        var cow = new Cow();
        var rooster = new Rooster();

        pig.Oink();
        cow.Moo();
        rooster.Crow();
    }
}

There are a couple problems with this. First of all, it can be made simpler. Wouldn’t it be easier if instead of having to call a different method for each animal, we could instead just call one for all the animals? Also, if we needed to mock this code, we would have one mock per animal. Instead, let’s implement an interface.

public interface Animal {
    public void Vocalize();
}

public class Pig : Animal {
    public void Vocalize() {
        Console.WriteLine("Oink!");
    }
}

public class Cow : Animal {
    public void Vocalize() {
        Console.WriteLine("Moo!");
    }
}

public class Rooster : Animal {
    public void Vocalize() {
        Console.WriteLine("Crow!");
    }
}

public class BarnYard {
    public void Noises() {
        var animals = new List<Animal>()
        animals.Add(new Pig());
        animals.Add(new Cow());
        animals.Add(new Rooster());

        foreach (var animal in animals) {
            animal.Vocalize();
        }
    }
}

Now the BaryYard will work with any type of animal and we only have one mock to build if we want to test it.

Let’s move to a more concrete example and use our FlightSim application and a .NET mocking framework named ‘Moq‘.

In the FlightSim.Service.FlightController class there is this method:

public bool IsDuplicateFlight(Flight flight)
{
    var flights = Get(flight.Number);

    if (flights.Any())
    {
        return false;
    }
    return true;
}

When the user creates or updates a flight, the system checks to make sure there are no other flights with the new flight’s flight number. To do this, the database is queried and duplicate flights are returned.

When we test this method, we don’t want the database to be queried because the machine running the unit test may not have access to a database. Also, testing with a database would go against our principle of only testing one thing at a time. So, instead, let’s test it using a mock.

using FlightSim.Data;
using FlightSim.Entity;
using Moq;
using System;
using System.Collections.Generic;
using Xunit;

namespace FlightSim.Service.Tests
{
    [Trait("Category", "FlightController")]
    public class WhenICreateADuplicateFlight
    {
        public bool _isDuplicate;

        public WhenICreateADuplicateFlight()
        {
            var mockContext = new Mock<IContext>();
            mockContext.Setup(c => c.Get(It.IsAny<Func<Flight, bool>>()))
                .Returns(new List<Flight>()
                {
                    new Flight("123", "Denver", "Pittsburgh", DateTime.UtcNow, DateTime.UtcNow, new Airplane("Cessna", 15, 5, 3))
                });

            var controller = new FlightController(mockContext.Object);

            var flight = new Flight("123", "Pittsburgh", "Denver", DateTime.UtcNow, DateTime.UtcNow, new Airplane("Cessna", 15, 5, 3));
            _isDuplicate = controller.IsDuplicateFlight(flight);
        }

        [Fact]
        public void IsDuplicateFlightShouldReturnTrue()
        {
            Assert.True(_isDuplicate);
        }
    }
}

You can see here, that we create an instance of the FlightController with a mocked instance of IContext. We use Moq’s ‘Setup’ method to mock the IContext’s ‘Get’ method. Now instead of connecting to and querying a database, a static list is returned to the application.

An especially powerful syntax is It.IsAny<>(). You can use this when your mocks are parameterized. It tells the mock to return the default value for parameter value. You can include a data type between the brackets to limit the parameter type.

That’s all for this post. I hope you enjoy unit testing and building mocks for your unit test.

Unit Testing the Right Way (or at least the less wrong way) Part 1

On this blog, I’ve covered automated UI testing and automated HTTP testing. This post will cover the bottom of the agile automated test pyramid, unit testing.

AATP

I will use a demo application named FlightSim to help us learn some basic unit testing techniques.

Each unit test should test one and only one unit.

In order to keep tests simple and to avoid ambiguity each test should test one and only one unit.

For example, in our FlightSim entities, we have IsValid() methods that determine if the entity is valid or not. Here is the Airplane entity.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FlightSim.Entity
{
    public class Airplane
    {
        public Airplane() { }
        public Airplane(String name, Int32 numberOfSeats, Int32 rows, Int32 seatsPerRow)
        {
            Name = name;
            NumberOfSeats = numberOfSeats;
            Rows = rows;
            SeatsPerRow = seatsPerRow;

            var errorMessages = this.IsValid();
            if (errorMessages.Any())
            {
                throw new Exception("Invalid airplane." + Environment.NewLine + String.Join(Environment.NewLine, errorMessages));
            }
        }
        public String Name { get; set; }
        public Int32 NumberOfSeats { get; set; }
        public Int32 Rows { get; set; }
        public Int32 SeatsPerRow { get; set; }

        public IEnumerable<String> IsValid()
        {
            var errorMessages = new List<String>();

            if (String.IsNullOrWhiteSpace(Name))
            {
                errorMessages.Add("'Name' is required.");
            }
            
            if (NumberOfSeats <= 0)
            {
                errorMessages.Add("'Number of seats' must be greater than 0.");
            }
            else
            {
                if (Rows * SeatsPerRow != NumberOfSeats)
                {
                    errorMessages.Add("'Rows' and 'seats per row' does not match 'number of seats'.");
                }
            }

            return errorMessages;
        }
    }
}

It is best to write a single test for each of the validations so that when one of those tests fails, we know exactly which validation needs looking at just by looking at the name of the test.

using FlightSim.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace FlightSim.Entity.Tests
{
    [Trait("Category", "Airplane")]
    public class WhenICreateAnAirplaneWithNoName
    {
        private IEnumerable<String> _messages;

        public WhenICreateAnAirplaneWithNoName()
        {
            var airplane = new Airplane("", 1, 1, 1);
            _messages = airplane.IsValid();
        }

        [Fact]
        public void ItShouldReturnOneErrorMessage()
        {
            Assert.Equal(1, _messages.Count());
        }

        [Fact]
        public void ItShouldReturnTheCorrectErrorMessage()
        {
            Assert.Equal("'Name' is required.", _messages.First());
        }
    }
}

If we test more than one validation per test, we would have to run the test in order to determine why it failed.

xUnit

There are many frameworks out there for performing unit tests and feel free to choose the one you like the best. For this post, we will use xUnit. I believe you will find this information helpful regardless of the framework you use.

I want to take look at the test above and explain what its doing, but first I need to let you know which NuGet packages you need to build this test.

  • xunit – this contains all the binaries you need for running tests and asserting values.
  • xunit.runner.visualstudio – you need this package so that your xUnit tests display in the test explorer. You only need to add this package to one of the projects in your solution.

Traits

You will notice this attribute added to the test class.

[Trait("Category", "Airplane")]

This attribute helps you organize your unit tests. If you organize the tests in the Test Explorer by ‘trait’, you will see the tests above listed under the heading of “Category [Airplane]”.

Constructor

The constructor for this class “WhenICreateAnAirplaneWithNoName” is executed before each of the tests run. It creates the test data and then executes the IsValid() method, saving its result in a global variable to be asserted later.

Fact

You will notice this attribute added to the two methods in the class.

[Fact]

This specifies the method as a test and adds it to the Test Explorer. When you select this test to be ran, it executes the constructor then the Fact method.

The nice thing about these tests is that if you read the constructor name followed by the name of the Fact method, you can understand what the test is checking, “When I create an airplane with no name, it should return one error message”.

Up next

In the next post I will show you how implementing interfaces can make your application code more testable. Then I’ll show you how to mock those interfaces so that we can test code that interacts with other applications.