I have had several "aha!" moments with unit tests, and have been burned several times by the lack of them. I have experienced how automated testing, especially unit testing, is a key building block for teams to iterate quickly and move fast. It's little wonder that all of the fast-moving tech companies I've worked at used this toolset company-wide - including Uber, Microsoft (the frequently-releasing teams), and Skyscanner.
While writing a testing section in my developer's career path book, I paused to think about the main benefits of unit tests, and how I see these build on top of each other. The first few benefits take short time to experience - validating your code is immediate - but other benefits take time. It makes me wonder if the reason unit testing is more popular with more experienced developers is that they've worked long enough with codebases to experience the longer-term benefits, that compound with the short-term ones.
Here's the list, with the stacked benefits of the unit testing approach.
- Validate your work. By writing a test, you double-check what you did. I caught so many mistakes this way - and I learned not to trust my own code, unless I double-check it.
- Separate concerns in your code. Unit testing your code requires you make it testable. Making code testable often means declaring dependencies upfront. This kind of structuring of the code usually leads to a cleaner design and better separation of concerns. With code that is not tested, it's easier to miss implicit dependencies and classes silently taking on multiple responsibilities.
- An always up-to-date documentation. While documentation will get out of date, unless you update it, tests that run and pass with the code will not get out of date. If you write clean tests, these tests can serve as evergreen documentation for the code. Fun fact: I usually start code reviews by reading the tests added/modified, to see if I can understand the changes just from the tests. If I cannot, that's usually a smell for me.
- Fewer regressions. I cannot count the number of times I made a code change that looked good to me - yet unit tests someone else wrote started to break because I introduced a bug. I also cannot count the number of times when we did not have unit tests for the code I changed, shipped the code, and discovered I've introduced a regression - weeks or months later. Unit tests do a remarkable job of catching regressions as long as they are in the codebase. Which brings me to the last point:
- A safety net for refactoring. While unit tests can catch regressions for small changes, they shine with large refactoring. In codebases with little unit test coverage, I have never felt confident making large refactors: simply because I have been burned by regressions upon regressions upon regressions so many times. On codebases with high test coverage and good tests, refactoring is much higher confidence work.
What are the benefits you've seen with unit tests short-term and long-term?