I’ve finished reading Growing Object-Oriented Softwared, Guided by Tests by Steve Freeman and Nat Pryce. The book is a good introduction into TDD. First it goes through core concepts and then demonstrates how to implement TDD on a simple project.
My takeouts throughout the reading:
- TDD is a core practice of XP. TDD is not about keeping defects from the users, but helping the team to understand the features and to deliver them reliably and predictably.
- Making code accessible for testing often drives it towards being cleaner and more modular.
- TDD cycle: red/green/refactor
- Different kinds of tests:
- acceptance - exercises the functionality we want to build. Does the whole system work? Whenever possible should be end-to-end without directly calling internal code.
- integration - does our code work against code we can’t change?
- unit - do our objects do the right thing, are they convenient to work with?
- External/internal quality: customer facing or developer/admin facing
- OOP focuses more on the communication between objects than on the objects themselves. An object has a method of handling every type of message that it understands and in encapsulates some internal state.
- Objects could have different semantics:
- values model unchanging quantities
- objects have identity that may change over time and model computational process
TDD best practices:
- First make a minimal “skeleton” of an app and set up deployment infrastructure. E.g. for a web app it would be a one page site which shows a field in database.
- Develop from outer boundary. E.g. write end-to-end acceptance test and start unrolling the logic from the incoming external message.
- The development cycle goes like this. When implementing an object, we discover that it needs a service to be provided by another object. We give the new service a name and mock it out in the client object’s unit tests, to clarify the relationship between the two. Then we write an object to provide that service and, in doing so, discover what services that object needs. We follow this chain (or perhaps a directed graph) of collaborator relationships until we connect up to existing objects, either our own or from a third-party API. We think of this as “on-demand” design: we “pull” interfaces and their implementations into existence from the needs of the client, rather than “pushing” out the features that we think a class should provide.