Skip to content

Unit Testing

How confident are you in your code? How difficult is it to add new features to your new application? How scared are you that bugs escape testing and make it to production?

These concerns are common, especially among existing teams with legacy applications. One of the biggest improvements team's can make to improve their code quality is to implement tools for static code analysis.

Unit testing is a fundamental practice to build a quality product. Using proper techniques along side code scanning in continuous integration, teams can increase their confidence in their application and develop new features quicker.

Teams with a robust unit testing practice have greater confidence to add new features without breaking older, existing features. It seeks to validate new and existing functionality at the code level and deliver fast feedback on failed scenarios.

Unit tests verify the smallest atomic pieces of the application. While adding new tests on existing legacy codebases, tests for new code is fast to create. Because tests are executed at the code-level without integration points, unit tests are extremely fast to run and repeatable across local environments and the CI build server.

Unit tests act as the first line of defense in automated testing

Why is unit testing valuable

  • Promotes safe changes
  • Executed with every build
  • Faster feedback for local development and pipeline
  • Protects the quality of the application
  • Promotes decoupled app design
  • Good for verifying logic
  • Prevents refactoring errors

Will writing unit tests speed up or slow down a team's delivery speed?

Principals for writing unit tests

1. Keep it simple.

Each test should have one and only one assertion. A simple, common pattern is known as Arrange, Act, Assert (AAA). This template gives a clear path to setup how unit tests can be written.

// arrange
var numA = 3
var numB = 7

// act
var sum = calculator.add(numA, numB)

// assert
assert.true(sum, 10)

2. Keep each test atomic.

Tests set themselves up and tear themselves down independently, and do not require other definition or tests to run. This includes pre-conditions, data setup, variable definitions, and mocking where needed.

3. Follow a standard naming convention.

Teams should decide on a consistent format to define tests. As with all good coding practices, tests should be self-documenting. All team members should be able to read a test report and understate what the test ran.

shouldExpectBehaviorWhenState()
Given_Preconditions_Then_Expected_Behavior()

4. Do not use external dependencies.

Unit tests should use a mocking framework to eliminate external dependencies. This helps reinforce the narrow scope of unit testing while maintaining fast, cheap, and inexpensive execution.

Common unit testing frameworks

  • JUnit, TestNG (Java)
  • Mocha, Karma, Jasmine (JavaScript)
  • NUnit, XUnit, MSTest (.NET)

What does a unit test look like?

function isCustomerAgeAtLeastEighteen(age) {
    if (x >= 18) {
        return true;
    }
    else {
        return false;
    }
}
assert(isCustomerAgeAtLeastEighteen(18), true);

Best Practices

  • Test for happy path scenarios and obvious edge cases.
  • Store tests in source control and in the same repository as the source code.
  • Execute tests locally before pushing to the build server.
  • Utilize proper TDD (Test Driven Development) practices