Unit Testing

15 Nov 2019

Unit Testing is the process of validating small units of code, to confirm their expected behaviour within a larger application

What are Unit Tests

Unit tests are written typically as separate methods to the primary application source code. The deployed release of an application would almost never contain unit test functions, unless it was appropriate in the domain to do so.

Unit Tests are typically grouped within classes to test related functionality, of a specific component of an application.

Unit Tests, when compiled and run by a Unit Test Runner intentionally exercise components of an application by using a set of pre-engineered and contrived inputs validate that expected outputs are received. Functions which take no arguments and return no results can also be tested to ensure that the functions complete without failure, and that the internal state of the class achieves an expected outcome.

A very simple example might be to validate that a Calculator Add function, when given two numbers 1 and 2 returns the value 3.

namespace Calculator.UnitTests {

    [TestFixture]
    public class CalculatorTests {

        [Test]
        public void Add_1plus2_Returns3() {
            // Arrange
            var calculator = new Calculator();
            var expected = 3;

            // Act
            var actual = calculator.Add(1,2);

            // Assert
            Assert.AreEqual(expected, actual);
        }

    }

}

In the above example, a Unit Test is validating a single input output scenario. Following the above technique adding another input output scenario would require another unit test method. In the real world, multiple combinations of inputs and outputs can be validated with a single method.

In reality testing is typically much more complex than the above contrived example. However if you understand the core principles of unit testing, the more complex variations are easier to understand as functions which are attempting to achieve the same objective. For a given input and/or input state, is the returned output and/or state as expected?

Who writes Unit Tests

Typically a standard developer is responsible for the creation and maintenance of unit tests. Sometimes teams will have a team who are responsible for the creation of Unit Tests within a development team. Tead Leaders may create high level unit tests to help guide the team to understand high level concepts.

Sometimes however support technicians will write Unit Tests to validate reported errors and their resolution.

Test Driven Development (TDD) typically uses Unit Testing to construct the Unit Tests before the code base functionality is created or modified. Using this technique, a developer will take the minimal path to ensure that the test case passes, and thereby avoid over engineering the code base.

Vocabulary

How Unit Tests work

Unit Test Runners are typically either built directly into an IDE, (Integrated Development Environment), or can be executed easily from the console or shell terminal.

By combining the build action with an immediate trigger to commence running the Unit Tests, a new build can be easily validated for readiness.

Continuous Integration assists the automatic build whenever code is commited back into source control, executing the unit tests to ensure that the code base has not been broken.

Designing Unit Tests

Unit Tests should attempt to respect the current Test Strategy, and avoid introductng complexity.

Unit Tests should consider

Unit Tests implement these above three primary concepts using

Unit Test structure

Well-written Unit Tests read like application documentation and can be used by developers to explain how the application should behave. For this reason, test methods typically have a method names which describes

When the naming convention is consistently used, errors raised during a test run can be easily identified and the expected result compared against the actual outcome, without having need to dig through excessive source code.

Internally, within the Unit Test, the test method is written in a consistent way to ease interpretation, modification and extension as required. Typically Unit Tests are written in the following order.

Code dependencies

Often real world scenarios are complex and require aspects which have extensive dependencies. Unit Tests should be written in such a way to avoid complexity, and so both the code base and the unit test may require additional work to avoid this complexity.

Avoiding Unit Test technical debt

Technical debt typically creeps into an application code base where the effort to maintain a component exceeds the payoff by correcting it. Several techniques can be used to avoid technical debt

Implementations

Language Unit Test Frameworks Stub and Mock Framework Test Runners
C# MSUnit, NUnit Moq, RhinoMock Visual Studio (vstest), JetBrains Resharper
Java JUnit Mockito, Cucumber, Spring, Robot Eclipse
TypeScript, JavaScript Jasmine, Mocha, Jest, Protractor (Angular io and AngularJs) JsMokito, TsMokito, typemoq Web Browser
C Unity, Automated Testing Framework typically included in base framework gcc, gxx, g++
C++ Boost, CppUnit, CppTest typically included in base framework g++

For a more complete list see Wikipedia - List of Unit Test Frameworks