Reading Clean Code Week 5: Unit Tests

Jacob on Software
Geek Culture
Published in
5 min readJul 28, 2021

If you ask any professional developer what the most import part of a software developer’s job is, I’d bet more than half of them would say testing your code. To be sure, Test Driven Development (TDD) is the way of the world in software development, and Clean Code dedicates an entire chapter to the topic. Even though it’s a relatively short chapter, the subject of unit testing is so important to the developer community at large it feels warranted to dedicate this week’s entire blog to it. In this post, I’ll discuss the Clean Code philosophy on testing and give some general guidelines on how an engineer can write good unit tests.

Martin begins this chapter by saying that when he writes tests, he slowly, meticulously steps through his code, isolating every nook and cranny of his program to ensure it behaves as expected every time. However, Martin says, although engineers have latched onto some of the most popular aspects of testing — such as writing test code before writing production code and automating tests — they sometimes fail to grasp the subtle, underlying thought processes behind testing. Delving more deeply into these principles, Martin lays out his three overarching rules of testing:

  1. You may not write production code until you have written a failing unit test
  2. You may not write more of a unit test than is sufficient to fail (your code not compiling is failing, by the way)
  3. You may not write more production code than is sufficient to pass the currently failing test

Martin writes that the lifecycle of these three rules is roughly 30 seconds. If you’re reading these rules and thinking “It seems like I’m going to have to write a lot of tests,” then you’re right. While it may seem daunting to write this many tests, writing clean tests ultimately frees you up to write virtually any production code you want.

In general Martin says our tests should follow these five rules, assigning them to an acronym called F.I.R.S.T.:

  • Fast: Tests should be fast. But tests don’t have to be lightning quick. Martin calls this the Dual Standard. Ultimately, you want your tests to be fast enough so that you actually run them, but they need not be as fast as your production code. After all, your users will never see your tests, so they don’t need to be as speedy as rendering your React code to the client.
  • Independent: As this brilliant blog post says, tests should be atomic. That is, they should test single, self-contained bits of logic in your code. This is related to Uncle Bob’s idea that each test should target only a Single Concept.
  • Repeatable: Your tests have to work regardless of which machine they run on. If your tests perform differently in different environments, you can’t rely on them to, well, test.
  • Self-Validating: As Martin says, unit tests should have a crystal-clear boolean output. You should be able to easily grok the outcome of your test, not have to scour the log files trying to intuit what your tests mean.
  • Timely: Maybe most importantly, tests should be written in a timely fashion. Remember, the whole point of testing is to test your production code. By writing your tests just before you write production code, you ensure that you write tests and design your production code to be testable.

To really drive home some of these points on testing, let’s look at a simple program and how to test it. For this example, I’m going to use one of Exercism’s coding challenges. Exercism is an online, free coding platform that offers code practice and mentorship in 50 different programming languages, and they use an extremely popular testing framework called Jest. More specifically, we’ll look at Exercism’s challenge on reversing a string. Below you can see both my code as well as the terminal output for the program:

reverse-string.js

Although we can visually see that this code works, it isn’t a very efficient way to test. For one, we’re breaking the Self-Validating rule of F.I.R.S.T. by verifying this code’s output in the console. We would be much more efficient (and cover a lot more of our code) by writing a comprehensive test suite, as the team from Exercism has done here:

reverse-string.spec.js

I’m going to avoid getting into the syntax of these tests for a very simple reason: these unit tests are intuitive! Any developer worth their salt could come into this file and see the six cases this suite is testing for, how we’re accounting for palindromes as well as capitalized words, among other things. As you can see, these tests also adhere to the Single Concept principle, each unit clearly isolating one potential input for our code to pass. While we could test a couple of edge cases — inputs that occur only at an extreme (maximum or minimum) frequencies — here, these units tests will catch a broad swath of errors. Let’s actually run our tests and see what we get:

output for reverse-string.spec.js

As we can clearly see, our test suite is extremely fast, and our code passed our tests (as an aside, I don’t think there’s anything so satisfying to a developer as green checkmarks next to your tests). Jest’s tests are excellent because they give a clear description of what was tested in the output as well as how long it took to test our code. With these tests, we’ve clearly and quickly demonstrated that our code is working order, letting us move onto our next task with confidence.

While testing might seem cumbersome and restrictive, Martin makes the point again and again that good tests free you up to write any code you want. Rather than plodding around in the dark wondering what your logic is doing, TDD keeps our code flexible, maintainable, and reusable. With clean, readable tests, we can change our code as we please, knowing that our logic is still sound because we’re still passing our tests.

As you might imagine, we’ve barely scratched the surface of testing in this blog. And as with almost anything in software development, there are tons of resources to learn testing in more depth, from Udemy courses to books to helpful blogs. In following Uncle Bob’s rules on writing clean tests, you’ll free yourself up to write any code you want… so long as it passes your tests.

--

--