Chandra Sivaraman
Software Engineering Notes

Unit Testing Part 2

Unit Testing

What does a unit test look like?

So, in part 1, we had an overview of what unit tests are and why they matter. Abstraction is a wonderful thing, but meaningless without something concrete to give it context. Without further ado, here’s what a unit test looks like (using C# and .NET Core):

using Xunit;

namespace UnitTestSamples.Tests
{
    public class MultiMapTests
    {
        [Fact]
        public void Add_KeyDoesNotExist_AddsValueToNewListForKey()
        {
            // Arrange
            var multiMap = new MultiMap<string, string>();

            // Act
            var key = "yellow";
            var value = "Sun";
            multiMap.Add(key, value);

            // Assert
            Assert.True(multiMap.ContainsKey(key) && multiMap[key].Contains(value));
        }
    }
}

The method Add_KeyDoesNotExist_AddsValueToNewListForKey on class MultiMapTests here is the unit test. We’re using an (excellent) unit test library called xUnit to run the test. The Fact attribute on the method indicates a test for invariant conditions.

We use a standard pattern here to structure the unit test called Arrange-Act-Assert. Arrange is where we do any setup of initial conditions required for the test. Act is where we exercise the SUT (System Under Test), which in this case is the Add method on the MultiMap class. Assert is where we examine the aftermath to determine whether the test succeeded or failed.

There is also another pattern used here to name the test. It is called Action-Scenario-Result. The Add in the test name is the action under test. KeyDoesNotExist describes the scenario being tested and AddsValueToNewListForKey is the expected result. In short, we’re testing whether calling the Add method with a non-existent key creates a new list with the value and adds it to the multimap.

Another type of test called a Theory is a parameterized test that can be used for varying conditions. The same test can be used for multiple input conditions (the InlineData attribute on the test method whose values correspond to the parameters of the method). Since we add only the "yellow" key to the map, that condition returns true while the non-existent "red" key returns false.

[Theory]
[InlineData("yellow", true)]
[InlineData("red", false)]
public void ContainsKey_VariousKeys_ReturnsStatus(string key, bool expectedStatus)
{
    // Arrange
    var multiMap = new MultiMap<string, string>();

    // Act
    multiMap.Add("yellow", "Sun");

    // Assert
    Assert.True(multiMap.ContainsKey(key) == expectedStatus);
}

As you can see, there is nothing terribly complex about writing unit tests. We’ll get into more involved tests that use mock objects in a future post, but even there, the idea is to keep the test as simple as possible. If our tests turn out to be overly complicated to setup, that’s a sign that the System Under Test (SUT) could use some refactoring.

Code

https://github.com/cs31415/unit-test-samples

Takeaways

Continue to part 3.