There are many books about software testing, but I will put Quality Code in the best that I have read. It presents a philosophy about software testing that has been mine in my software developer life: the programmer is the main person responsible for the quality of its code.
The author describes the goal of his book as “This book is about easily and routinely testing all of your software. It primarily focuses on unit tests, but many of the techniques apply to higher-level tests as well. This book will give you the tools – the implementation patterns – to test almost any code and to recognize when the code needs to change to be testable.” This book is aimed at professional software developers and it provides a lot of practical examples in code that is readable even if you don’t program with this specific language.
This practical part is preceded by an introduction that describes the principles and theory of software testing, discussing for instance the different nuances of the black and white approaches or explaining how some programming patterns make the code difficult to test. My favorite part is indeed this chapter devoted to design and testability. Everything is presented in a way that is easy to read, even the parts with meaningful code examples.
Quality Code is a book that I will strongly recommended to every software developer that is convinced that code quality is his main responsibility and not something that will be eventually managed by a QA department elsewhere before delivery. This is a great book on how to grow code and quality together.
Reference: “Quality Code: Software Testing Principles, Practices, and Patterns”, Stephen Vance, Addison-Wesley, 218 pages, ISBN 978-0321832986
An example provided in one chapter of this book is available on https://github.com/srvance/QualityCode14
However, as I have coached teams into testing regimens, I have repeatedly found that the obstacle to adoption is neither a lack of understanding of the process flow, nor a misunderstanding of the concepts, nor an insufficient lexicon, nor skepticism about the value of the practices. All of these obstacles exist in various people at different times, but the most common one that has not been well addressed is simply limited understanding of the mechanics of testing code and of writing testable code.
Of all the practices you can leverage to assist your craftsmanship, you will get the most benefit from testing. You may say, “We have been testing software for years, and we still have lots of bugs.” Therein lies the key to going forward. In a Lean system, you try to prevent defects rather than catch them. The tradition of software testing has been one of catching defects. It should not surprise us, then, that we have an inefficient process. The earlier you test, the sooner you catch defects. Does that mean you are still just catching defects? If you write the tests after the code, then yes. If you close the gap between creating and catching the defects, less change occurs between the two events. You have a higher likelihood of the context of the created code being fresh in your mind. If you write the test immediately after writing the code, it can verify your conceptions and assumptions and keep you on course. You also have an opportunity to really catch the bugs before they occur, however. Test-first and test-driven approaches to development bring the testing before the coding. When you test first, you capture your intent in an automatable and executable form. You focus on what you are about to write in a way that works to prevent defects rather than create them. The tests you write serve as a persistent reinforcement of that intent going forward. In addition to helping you do the thing right, it helps you to do the right thing. Remember, it is still a bug if you implement the wrong thing really well.
As discussed in Chapter 3, all software has intent, and the first goal of testing is to verify the intent. Black-box testing is popular and effective because it verifies software purely by its intent rather than its implementation. However, most moderately complex algorithms or logic cannot be completely tested purely as a black box. Coverage-guided unit testing requires white-box insight into the implementation. Notice that this principle says “intent over implementation” rather than “intent instead of implementation” as you find in more prescriptive statements. It is advisable and necessary to guide some level of testing, particularly unit tests, against the implementation, but you should never lose sight of the intent that the code is trying to produce.