The term “Clean Code” has been widely known at least since the publication of Robert C. Martin’s book of the same name in 2008. However, as in the book, Clean Code usually refers only to production code. At the same time, automated tests became increasingly widespread and important due to movements such as TDD and CI/CD. Modern software projects are unthinkable without a high level of test coverage. This means that a large portion—or even the majority—of the codebase consists of test code. This article attempts to apply Clean Code principles such as maintainability, readability, and extensibility to test code. The examples in this article are written in Java and therefore use JUnit and AssertJ, but the underlying concepts are also transferable to other programming languages, test frameworks, and assertion libraries.
Let’s assume the first step has been taken and it is accepted that the quality of test code is important. Can the quality requirements for production code be applied one-to-one to test code? Unfortunately, no. Test code serves a different purpose than production code, which means the requirements also vary. Therefore, we first define the quality requirements for test code and later build on that foundation.
A fundamental quality criterion is the comprehensibility of test code. Unlike production code, which is meant to express what the code does, test code is meant to express which requirements the software fulfills. Depending on the level of testing, a different level of abstraction is appropriate. While lower-level tests of individual methods can directly invoke those methods, higher-level acceptance tests should abstract far enough from the code that they can also be understood by subject matter experts.
In addition to comprehensibility, maintainability is another important quality criterion. As with production code, maintainability can be understood in different ways, but in essence it means how easily changes can be made. Traditionally, this means how easily test code can be adapted to changing requirements—just like production code. However, when it comes to test code, another aspect comes into play: refactoring of production code. Refactoring means that the code is changed, but the software still fulfills the same requirements. Since the requirements are implemented in the form of test code, maintainable test code should not need to be changed when the production code is refactored.
The last quality criterion we consider is extensibility. Comprehensibility and maintainability also foster better extensibility, but extensibility introduces an additional, distinct dimension. For production code, the Open-Closed Principle (OCP) is the most well-known principle for promoting extensibility. OCP states that software components should be open for extension but closed for modification. When OCP is applied to production code, this also indirectly improves extensibility of test code. However, OCP can also be applied explicitly to test code.