As a junior programmer, you might be confused by unit tests. To make things worse, tests used as examples often make their purpose more unclear. When you see stuff like
you are right to doubt whether there is a solid reason to spend any time writing them. Below, I show my reasons for writing unit tests.
Unit tests are simple scripts that check whether a given unit—class, function, module, etc.—is working as expected. They are meant to be rather simple, to cover the happy path of the code plus a few edge cases. They contribute to the long-term success of a project because of the reasons I discuss below.
When you start building applications, the most natural thing is to test the code with the user interface. You can make this process way faster and more reliable by writing a script that will check the code for you. With tests in place, rerunning all of them takes no mental energy from you; you can do it as often as you feel like. This leads to a shorter feedback loop too, which will help you stay focused and productive.
Writing unit tests makes me think about edge cases—all the situations that are rare, unexpected, or wrong. When you write the logic, it’s normal to focus on the happy path, or what’s normal and expected to happen. When you write tests, you can set up checks for the edge cases and define what should happen in each of them. This makes your code more resilient in cases of unexpected inputs.
When you add unit tests to your code, you see what is easy to test and what is not. As your code grows in size and complexity, tests will force you to break it into manageable pieces. This is great because it will help you take the quality of your code to the next level. Every segment that received excessive responsibilities will require exponentially more complicated unit tests. In those cases, it’s a good idea to stop and rethink how you organize your logic.
Your tests will become an additional resource for the next person working the code to figure out what it’s doing and how it’s supposed to work. It’s a kind of extra documentation, with bonuses:
- it’s often more precise than written descriptions in the actual documentation.
- tests can be run against the current code to make sure all statements are still valid; you don’t depend on a human to read, understand, and double-check the code.
Unit tests execute so quickly that it’s a no-brainer to run them on any update, no matter how small the changes seem. You can set up your repository to run continuous integration (CI) and accept only changes that pass all the tests. In this way, you can ensure smooth change integration no matter the update:
- minor update that “shouldn’t break anything.”
- third-party library update
- quick and dirty attempt at some fix
Unit tests protect the codebase against all minor regressions that they are covering.
Unit tests are an essential part of maintaining high-quality code. You can see it as one of the legs of a table:
Sure, you can remove it, but it will make keeping a balance more complicated. Write them, and they will help you achieve good code quality and make your application safer.