There is still too much code without automated tests in place. It is a pity since automated tests are the foundation that safely enables rapid development.
The usual complaints I hear are
Do you recognize the complaints? I used to have some of them, but the benefits outweigh the pain.
Tests are only relevant when we need to make a change to the code. If there are no tests in place, we can’t know if our change had any harmful effects on the system. We have no safety net!
On a shorter timeframe, we could make our change and see that we implemented a mess, but the tests validate that it does work as expected. We can not refactor the code to be more straightforward and pretty and continuously keep an eye on the unit tests’ green lights.
More reasons that tests are worth spending time on.
Creating tests before the code, as the Test-Driven Development philosophy dictates, has multiple benefits. You will start coding with the perspective of the user of your code. It will improve the usability of the interface. You will not have the option to skip testing in the end because of time pressure.
Have you ever tried to look into a piece of code not daring to refactor so you just add the code you need to implement a feature? Now this area of the code is even harder to change when the next feature needs to be implemented. Tests make it possible to avoid this pitfall. With the safety net from a test harness, you can refactor the code and ensure that you don’t break existing functionality.
In many cases, it is difficult to exercise code in a running system. It could be that it is only triggered in special circumstances. So, testing changes are time-consuming. Unit tests avoid this and allow you to test continuously.
There are many benefits to having tests in your code. But how do you go about writing them? If you Google it, you will find many different philosophies on what to do. Start with the following tips. They are the steps I use when programming.
I often see that tests are not written because of time constraints or not prioritizing them. The simplest way to avoid it is to write the tests before the code. In this way, they can’t be forgotten or skipped. In the beginning, it felt unnatural for me to do it that way. But with practice, it slowly shifted.
The worst mistake I made when writing tests was to test the implementation behind the code instead of the behavior. It makes the test difficult to understand, and any changes to the implementation will force the test to change as well. A better way is to focus on testing the interface without knowing the implementation details. If you follow Tip #1 it is easier to do. But it will take some time to get used to.
It is natural when programming to end up writing test cases that are tightly coupled with the implementation. But we should try to test the behavior of the code instead. One way to trick the mind into this state is to start the name of the test case with “Should” like this: Should_ExpectedBehavior_When_StateUnderTest
Examples of names: