Why do we write unit tests? What do we expect from them?
I have heard many different opinions, sometimes quite opposite. Some may argue that unit tests are not that important, and sometimes are just a burden that prevents from moving fast. Others claim that they actually allow to iterate faster and they are more important that the code itself.
I think that the truth lies somewhere in between. When written poorly, unit tests will indeed will create obstructions, draw resources and slow you down. When done right, unit test suite will allow you to go fast and don’t look back checking if you have broken anything.
It takes skill to do it right, but first and foremost, it is crucial to identify what is the expected result, what do we want from unit tests.
In my personal experience, I have seen following benefits from good suites of unit tests:
Verification of correctness. The obvious way to check if code works is to run it. It is OK to manually run and verify the result as long as the application is quite primitive. As soon as it becomes bigger than several logical structures, manual verification is no longer a valid option. But we can use other code to run our code - tests. The test will run the desired piece of code and verify if it does what is expected.
Bug fixing. Test don’t fix bugs themselves, but they are the best tool to narrow down the problem and to ensure the fix works (and also that it keeps working in future). You can write a test for a piece of code under suspicion and make test validate if the result produced by code matches expectation. If it does - code is OK, if not - even better - the problem has been found. Now keeping the test in place, you can fix the bug and the test will tell you right away the problem is gone.
Better code structure. Writing tests is useful to split code into smaller, more meaningful functions with sole responsibility. Especially if you follow TDD, the code will naturally grow better-structured with proper dependency injection and separation of concerns.
Documentation and specification. Unit tests are a great tool for code documentation and specification. A good well-written suite of unit tests can tell much more about how code works. It clearly states what are the possible inputs and expected outputs. Moreover, a suite of tests will always be up to date, it cannot become neglected like a wiki page.
Facilitate changes and enable refactoring. It may sound counter-intuitive, but a good suite of tests makes it easier to make changes in code. Good tests verify only interfaces, not implementation details. It gives engineer an opportunity to change internal implementation and still be confident that the result is correct. As soon something breaks, the tests will tell exactly what. This makes a feedback loop very short. Unit tests allow to make changes safer and faster.
Communication. Unit tests is a great communication tool. As mentioned above, unit tests are a great documentation. And it stays relevant over time. A good suite of tests communicates author's intention, what the component does and to expect from it, both to other team members and to the author himself (try to remember intricate details of behavior several years after the release!).
Overall, in my experience unit tests proven to be an extremely valuable tool that gives me ability to build systems that are error-free and are easy to maintain. It may take time and require a paradigm shift to fully appreciate all benefits of good unit test suite, but the reward is great. A good suite of tests is a good investment that pays off in a long run.