Self-testing code refers to the ability to run a series of automated tests against the codebase and be confident that passing these tests indicates the absence of significant defects in the code. In a sense, alongside building your software system, you concurrently construct a bug detector capable of identifying any faults within the system. If a team member unintentionally introduces a bug, the bug detector alerts you. By regularly running the test suite, ideally multiple times a day, you can swiftly identify such bugs soon after their introduction, simplifying the process of locating them by examining recent changes. In our view, any non-trivial code lacking tests should be regarded as flawed.
Self-testing code plays a crucial role in Continuous Integration. In fact, we argue that true continuous integration cannot be achieved without the presence of self-testing code. It is also an essential component of Continuous Delivery.
One evident advantage of self-testing code is its potential to significantly reduce the number of bugs that make their way into production software. At the core of this benefit lies the cultivation of a testing culture in which developers naturally consider code and tests as interdependent entities.
However, the most significant advantage of self-testing code extends beyond merely avoiding production bugs; it lies in the confidence it instills when making changes to the system. Old codebases often evoke trepidation, with developers fearing to modify functional code. Even rectifying a bug can be perilous, as it may inadvertently introduce more bugs than it resolves. In such circumstances, the process of adding new features becomes excruciatingly slow, and the team becomes apprehensive about refactoring the system, ultimately accumulating technical debt and descending into a deteriorating cycle where fear of change intensifies with each modification.
With self-testing code, the scenario is different. Team members are confident that they can safely address minor issues and improve the code because, should a mistake occur (or rather, “when I make a mistake”), the bug detector will alert them, enabling quick recovery and resumption of work. With this safety net in place, developers can dedicate time to maintaining code quality, leading to a virtuous cycle in which they become progressively more efficient at introducing new features.
While these benefits are often associated with Test-Driven Development (TDD), it is essential to distinguish between TDD and self-testing code. TDD represents a specific practice that yields self-testing code as one of its advantages. I personally endorse TDD as a valuable technique. However, it is also possible to achieve self-testing code by writing tests after writing the code, although the work is not considered complete until the tests are in place and passing. The crucial aspect of self-testing code is having the tests, regardless of the specific method employed to develop