I plan to write some articles regarding how to write good unit tests but before providing what my guidelines are regarding unit testing it’s important to first understand what are the reasons why we want to unit test and what are the drivers for depicting these guidelines. Because my objective is to encourage developers to practice unit testing, I choose to address the main oppositions I encounter today on the field.
Unit testing is not productive!
If we take a closer look to how productivity is optimized in classical manufacturing process, we can envision why well written, well maintained unit tests have exactly the opposite effect.
The productivity of a factory is measured by the speed at which products flaws from the production line and the effectiveness of the production line. As one may think, the speed at which products flaws out of the factory is not the average speed of each part of the production line but it depend mostly on of the number of things to process in the production line. So if you want to increase the overall production capacity of a factory you’ve to synchronize every part of the production line and make sure that each part works at a constant peace and that every part is working at a sustainable piece for him and his neighbor. The worsted thing that can happen in a production line is that a defect is caused in a part of the line and is paced at the next part. The defect will not only cause the part of the line where the defect is detected to stop but the defect part has also to be resent to the part of the line where the defect was made. This will desynchronize the overall production line and diminish the overall productivity of the factory. When we produce software the same is true, if a bug is detected by the testing team or worse in production it will generate a lot of waste. The bug will need to be described precisely; the developers will need to switch from their ongoing task to the bug resolution. A lot of time will be loosed in understanding the problem. The bug resolution will need to be tested. Finally a patch will need to be deployed in production potentially causing a service interruption. There is also a consequent risk to repeat this process the defect was not corrected adequately or because a new defect is caused by the resolution. An important side effect for unit test is also that they reduce the risk of a project. Each unit test is an insurance that the system works. Having a bug in the code means carrying a risk. Utilizing a set of unit tests, engineers can dramatically reduce number of bugs and the risk with untested code.
Unit tests will also decrease the maintenance cost because they provide a living documentation. This is called “Test as documentation”. Unit testing provides a sort of living documentation of the system. Developers looking to learn what functionality is provided by a unit and how to use it can look at the unit tests to gain a basic understanding of the unit. Unit tests embody characteristics that are critical to the success of the unit. These characteristics can indicate appropriate/inappropriate use of a unit as well as negative behaviors that are to be trapped by the unit. A unit test case documents these critical characteristics.
So it’s true that unit tests are generally doubling the initial cost of the implementation phase because it tends to cost the same amount of time as writing production code. But this cost is more than re-gain because the other steps of the production process shorten. The amount of defects detected by the Q&A team is drastically falling and a lot of time is won because the Q&A team can work faster. Even the overall throughout put of the development teams increases at the end because a time is not loosed anymore in correcting a lot of defects detected by the Q&A team. The project manager is far better at estimated the project status. At the end the trust of the business increases because they get features build on a constant pace and because the overall delivered quality increases.
Unit testing does not catch all bugs!
Unit testing and other forms of automated testing serve the same purpose as the automated testing devices in manufacturing. Unit tests will enable to detect rapidly a defect when code is changed or added. The automated tests are not made to detect malfunctions in production but to prevent that defects could enter into our assembly line. The real value of Unit testing & TDD is not that they can detect defects but that they overcome defects to happen! Unit testing will not only improve the quality perceived by the business because lesser defects will slip through it will also improve the internal quality attributes of the code itself because the developer will tend to refactor a lot more and will design his code more loosely coupled. (see Design for testability).
Nevertheless Unit testing alone is not sufficient, testing should happen on all levels but unit tests decrease the amount of other kind of testing that is needed. Because unit testing helps to eliminate uncertainty in the units themselves they enable a bottom-up testing style approach. By testing the parts of a program first and then testing the sum of its parts, integration testing becomes much easier.
Testing is for the testers!
Unit testing and other forms of automated testing serve the same purpose as the automated testing devices in manufacturing. Unit tests will enable to detect rapidly a defect when code is changed or added. The automated tests are not made to detect malfunctions in production but to prevent that defects could enter into our assembly line. The real value of Unit testing & TDD is not that they can detect defects but that they overcome defects to happen! Unit testing will not only improve the quality perceived by the business because lesser defects will slip through it will also improve the internal quality attributes of the code itself because the developer will tend to refactor a lot more and will design his code more loosely coupled.
When software is developed using a test-driven approach, the Unit-Test may take the place of formal design. Each unit test can be seen as a design element specifying classes, methods, and observable behavior. By writing your tests you are performing an act of design and all professional developers should aim for good design.
Unit testing is a waste of time because they tend to break and we constantly have to fix them!
Unit testing allows the programmer to refactor code at a later date, and make sure the module still works correctly. The unit tests enables refactoring because they provide a safety net that allows us to practice refactoring. The procedure is to write test cases for all methods so that whenever a change causes a fault, it can be quickly identified and fixed. Readily-available unit tests make it easy for the programmer to check whether a piece of code is still working properly. They enable us to constantly improve our SUT by adhering to the DRY principles. This principle does not only apply to our SUT but also to our test code. By constantly keeping our testsDRY by eliminating duplication we improve the maintainability of our tests and make sure that these tests stay efficient. When you spent too much time in fixing tests you should consider to review your test design. Have a look at the following articles these will provide some guidelines that will help you in increasing the maintainability of your tests.
This code is too difficult to test!
Because unit tests forces us to exercise our code in another context as the context in which the code will run in production – the tests forces us to design our code so that it is more loosely coupled. Loose coupling tend to improve reusability and robustness. Reusability and robustness are certainly desirable goals. So Unit testing is a way to assert that our code is robust and reusable – if your code is hard to test this is mostly because there is something wrong with your design and you should not try to test bad design but you should fix it!