Category: Test Code Quality

  • Sensitive assertions

    Good assertions are fundamental in test cases. A bad assertion may result in a test not failing when it should. However, a bad assertion may also cause a test to fail when it should not. Engineering a good assertion statement is challenging—even more so when components produce fragile outputs (outputs that change often). Test code should…

  • Fixtures that are too general

    A fixture is the set of input values used to exercise the component under test. As you may have noticed, fixtures are the stars of the test method, as they derive naturally from the test cases we engineer using any of the techniques we have discussed. When testing more complex components, you may need to build several…

  • Bad handling of complex or external resources

    Understanding test code that uses external resources can be difficult. The test should ensure that the resource is readily available and prepared for it. The test should also clean up its mess afterward. A common smell is to be optimistic about the external resource. Resource optimism happens when a test assumes that a necessary resource…

  • Unclear assertions

    Assertions are the first thing a developer looks at when a test fails. A good assertion clearly reveals its reason for failure, is legible, and is as specific as possible. The test smell emerges when it is hard to understand the assertions or the reason for their failure. There are several reasons for this smell…

  • Excessive duplication

    It is not surprising that code duplication can happen in test code since it is widespread in production code. Tests are often similar in structure, as you may have noticed in several of the code examples. We even used parameterized tests to reduce duplication. A less attentive developer may end up writing duplicate code (copy-pasting…

  • Test smells

    In the previous sections, we discussed some best practices for writing good test code. Now let’s discuss test smells. The term code smell indicates symptoms that may indicate deeper problems in the system’s source code. Some well-known examples are Long Method, Long Class, and God Class. Several research papers show that code smells hinder the comprehensibility and maintainability of software systems (such as the…

  • Tests should be easy to change and evolve

    Although we like to think that we always design stable classes with single responsibilities that are closed for modification but open for extension (see Martin [2014] for more about the Open Closed Principle), in practice, that does not always happen. Your production code will change, and that will force your tests to change as well.…

  • Tests should be easy to read

    I touched on this point when I said that tests should have a clear reason to fail. I will reinforce it now. Your test code base will grow significantly. But you probably will not read it until there is a bug or you add another test to the suite. It is well known that developers…

  • Tests should be easy to write

    There should be no friction when it comes to writing tests. If it is hard to do so (perhaps writing an integration test requires you to set up the database, create complex objects one by one, and so on), it is too easy for you to give up and not do it. Writing unit tests…

  • Tests should have a single and clear reason to fail

    We love tests that fail. They indicate problems in our code, usually long before the code is deployed. But the test failure is the first step toward understanding and fixing the bug. Your test code should help you understand what caused the bug. There are many ways you can do that. If your test follows…