Pragmatic Unit Testing by Andrew Hunt and David Thomas is a great example of how you present a large and important subject in plain English, with no marketing pitches, political dogma or religious fights. It covers methodology of test-driven development (TDD), but in contrast to thick volumes of boring academia, it gets right to the point. It is quite a skill to fit it all in only 150 pages!
Unit testing may seem daunting and therefore hard to get sold on at first. This book smoothes corners by helping you assess what may break, whether it is worth to test something in the first place, how to make sure that tests probe your code from every angle (accuracy of results, boundaries, inverse relationships, error conditions, etc). The authors also included a helpful check list of the do’s and don’ts of testing methodology.
Some books go overboard with source code, trying to embed development of an entire application in a book, and you end up reading lines and lines of code of every module and layer until it gets very boring. Pragmatic Unit Testing has just enough concise examples.
I especially enjoyed Chapter 6: Formalizing Mock Objects. If your code has dependencies which are not within your immediate reach (database, external systems, etc), learn how you can emulate them.
Some good points from the book:
- Each test should run independently of every other test.
- Validation (my pet peeve). In a well-designed system, you establish up-front the parts of the system that need to perform validation, and localize those to a small and well-known part of the system[…] Any part of the software system needs to be robust, and not allow any incorrect or unvalidated data through.
- Real testing checks results.
- By writing the tests first, you have now placed yourself in the role of a user of your code, instead of implementor of your code. From this perspective, you can usually get a much better sense of how an interface will really be used, and might see opportunities to improve its design.
- You can keep your code well-factored (i.e. “shy”) and easier to maintain by explicitly designing code to be testable.
- Don’t go overboard testing things that aren’t likely to break, or repeat essentially similar tests over and over just for the sake of testing.
- Testing legacy code. It’s better to add tests for the most broken stuff first, to realize a better return on investment of effort[…] And it doesn’t have to cover the entire legacy code base, just the painful parts.
- Don’t waste time testing aspects that won’t help you. Remember, you don’t want to create tests just for the sake of creating tests.
- Tests cannot rely on anything in the external environment that isn’t under your direct control.
As always, there’s a funny twist. To cite Bookpool: Andy Hunt and Dave Thomas have more than 50 years combined experience, developing software for clients around the world
. “Combined experience”? Is it like serving 63 consecutive life sentences? :) Take a line-up of authors from some Wrox book, add up their experience years and you get… Whoa!