Test Code Smell: When does your code start smelling bad ?

Erlon Almeida
4 min readFeb 5, 2024

What does the term ‘test code smell’ mean, and why should every developer pay attention to it?

Developers, listen up! Paying attention to clean code is not only essential for production but equally critical for your test code. As Kent Beck, a Test-Driven Development (TDD) pioneer, emphasizes, tests serve as living documentation, providing an accurate reflection of your system’s expected behavior. TDD not only simplifies refactoring but also offers immediate feedback, enabling early issue detection and long-term time savings.

In line with this, Robert C. Martin underscores the importance of readable tests, stressing their significance alongside clean production code. Poorly designed tests can impact flexibility, maintainability, and code reusability, making adaptation to evolving codebases challenging. Despite the initial time investment in writing tests, the payoff includes early bug detection, streamlined refactoring, and significant time savings over the long term.

Influential books, such as Kent’s on Test-Driven Development and the Clean Code manifesto, set the stage. Simultaneously, researchers like Rompaey, Neukichen, and Birsanz introduced the term “Test Smell.”

So, what exactly is a “Test Smell”? It’s a way of describing poorly written test code that goes against software development best practices, making tests less stable and harder to maintain. Characteristics include assumptions about external resources, overly complex tests, and signs of redundancy, all impacting repeatability and stability.

In essence, Test Smells are red flags in your test code, indicating potential problems. As developers, addressing these issues is crucial for maintaining a robust and effective codebase. Stay tuned for more insights into the evolving world of software development practices!

There are currently about 20 types of poorly practiced coding patterns documented, specifically related to unit test codes. I won’t go through all of them here because it would be quite tiresome. The intention is to show everyone that the way you’ve been writing code doesn’t go unnoticed. Speaking from personal experience, when you start giving names to these things, it becomes easier to identify these mistakes during your work. I’ll present some of them here, and reflect on whether you agree or not.

  • In the realm of unit testing, there’s a subtle yet significant issue that developers often encounter — the Conditional Test Logic Smell. Why is this a cause for concern? Well, statements like for, if, while, and switch embedded within a test method can alter the test’s behavior, elevating the likelihood of test failures under specific conditions. Take a glance at figure below, a code snippet extracted from a repositories residing on the GitHub platform.

In this snippet, the Conditional Test Logic Smell manifests itself within the list comprehension of Python code. The incorporation of conditional statements in this context can introduce unexpected complexities, potentially leading to unreliable test outcomes. As we delve into the nuances of this particular test code issue, let’s consider the implications and explore alternative approaches to maintain cleaner and more effective unit tests.

  • Exploring the Enigma: Unraveling the Unknown Test Smell

his particular type of smell surfaces when a test case contains code, yet lacks an explicit assertion linked to the production code. Check out in this second figure.

Imagine a scenario in Java where the test result can be discerned through a “throw Exception,” but the absence of an “assertion” statement renders the code more cryptic. This not only makes the test case harder to comprehend but also introduces additional design issues, such as the presence of a ‘for’ statement. As we dissect this example, it becomes apparent that it not only epitomizes the Unknown Test Smell but also intertwines with the Conditional Logic Test Smell. Does it make sense to you ? I know this concepts at prior seems like nonsense, but if you imagine that your code is gonna be read by other dev group, these issues will increase a lot the performance, according to my experience of course.

  • General Fixture Test Smell: this test smell rears its head when not all attributes declared in the setup fixtures of a test class find utility across all methods. To put it simply, each test case taps into only a portion of what’s declared in the setup, resulting in a patchwork utilization that can lead to comprehension problems. The cause-and-effect relationship between setup attributes and test methods further muddles the waters, making the expected results less visible.
  • Assertion Roulette: This specific test smell raises its head when a test case has multiple assertions, each lacking clear identification of its intention. Picture this scenario: multiple assertions within a single test case, each vying for attention without a distinct purpose. The consequence? A tangled web of intentions, making it arduous to comprehend why a particular assertion might fail….

Hey folks, I wanted to convey the central idea of when the code starts to smell, more for awareness than explaining each one to you. The goal is to make you aware of some of the key smells that we might code without even realizing how it can hinder someone else evaluating the code. For more information on this subject, I’ll leave a paper released in 2021 here.

So, that’s it, until next time!

--

--

Erlon Almeida

Msc. Software Engineer, LLM specialist, Python fluent