“One of the biggest failed projects I worked on had unit tests.”
If your looking for a recipe to be successful or an explanation for failure, the answer resides not in the technologies you choose or not choose.
Unit testing is a concept introduced by Kent Beck in Smalltalk.
It comprises the expectations that you have for every little part of your system that may break.
Checklist for good unit tests:
- they are automated
- they are repeatable
- they are easy to implement
- they remain for future use, once written
- they can be run by anyone
- they can be run by the push of a button
- they run quickly
Unit Tests must be
- trustworthy (they are bug-free as much as possible, if they fail, they do that always not only sometimes)
- readable – a best practice is that a test does not include control structures
- maintainable
Once you read this book, you may discover a lot of faults in your way of thinking about unit tests.
Most programmers write Integration tests, not unit tests
The first thing that struck me (because I undergo this problem at work) is that most of us write Integration tests, not unit tests.
In other words, we are not testing the results and the behavior of a single method or class, doing integration testing instead – we test how two or more things work together, not how each of them works independently of the other. Of course, integration testing and, moreover, continuous integration, are also very important, but the best practice is to separate integration tests from unit tests because the former are much slower. Any programmer should be able to run the unit tests as frequently as possible, if not before each commit. If running the tests takes too much time, the lazy programmers will start skipping it. So, it is great if you have a safe green zone for rapid (unit) tests, and a zone of slow tests that can be run during automated builds, for instance.
Avoiding doing integration testing instead of Unit Testing, leads us to the concept of stubs. External dependencies (objects in your system that your code interacts with, but over which you have no control) should be replaced by stubs, because Unit Testing aims testing the system without external dependencies. In turn, this idea requires an ability to change the object that represents an external dependency (filesystem, time, threads, memory) easily, at run-time – so we have to inject these objects to our system under test.
There are several techniques for that:
- replace the dependency with an interface of it that has methods that return the expected results
- inject stub implementation under class under test
-
- inject in the constructor
- inject by getters/setters
- inject as parameter to a function
- inject as factory class which creates the stub
Needless to say, Unit Testing !== Test Driven Development