I finished Growing Object-Oriented Software Guided by Tests (GOOS) this afternoon. It’ a book that I sincerely hope I would have had the patience to read years ago. It answers one deceivingly simple question in software development that has bothered me for a long time: How to practice TDD (Test Driven Development)?
When I first heard about the Golden Rule of TDD – “Never write new functionality without a failing test”, I was quite skeptical about it. But after several years building software a big company, I completely recognized why TDD was so appealing. My company has very strict software development procedures that require lots of meetings and documents before really writing a single line of code. But there wasn’t even one project that I worked on finally proved these times were not absolutely wasted. This kind deep frustration made me I realise that it was the effort to try to put the behavior of software completely in documents before writing any code is simply hopeless when it comes to thousands of hours of programming. We simply couldn’t image exactly how the software with size actually works.
TDD has a different view of software development. Instead of building software with a rigid, predefined blueprint, it thinks software as a plant that could grow big in a naturally, just like a tiny seed could sprout and become a huge tree. We know it’s going to be tall, strong and beautiful, but we can’t really predicate how exactly it looks like.
I like this idea so much that I more or less tried it in all my following projects, even my ex-girlfriend’s homework, after I finished the classical TDD book, Test Driven by Example. TDD did help, but I still felt uncomfortable about the final result from time to time. And I couldn’t really convince my colleagues that it was necessary to write test before writing code. As I became a senior engineer, I enforced TDD in my team. However, as soon as I left the company 3 years ago, my former colleagues immediately dropped all those TDD rules and went back to the “write code – manual test” cycle. But I don’t blame them. To really benefit from TDD, there are a lot of difficulties to overcome. However, I couldn’t see how to address these problems properly at that time.
TDD was difficult because:
- The test code often become messy after a while
- Testing with 3rd party code, like persistence layer, is slow and
- Refactoring code without certain design principles in mind often lead to chaos even if the size of code
- Mocking objects are really tedious to
- Asynchronous code are hard to
- Tests are too fragile, simple change in requirements could break many test cases
- Testing straight forward code make programmers feels they are wasting time
- Direct dependencies (new objects everywhere) makes mocking difficult
These problems are why like the book, GOOS, so much. It’s full of real experiences of TDD accumulated in the battlefield. In my work, I’ve met almost all the difficulties this book covers. And I have to admit that the approaches the author took to deal those problems are indeed better than my rushed solutions.
However the most important thing this book taught me is that while TDD could make better software, that doesn’t come free. It requires very careful consideration of how to refactoring, great patience to make test code readable and maintainable, and experience to decide what and how to test. It’s not a magic that could summon beautiful codes from the emptiness. It’s a skill and an art like painting that must be learned by hardworking and practicing.
Yes, TDD is hard. But as far as I can see, it’s still of best choice to coup the monstrous complicity in modern software systems. If you have strong desire to write clean code as I do, I highly recommend you have a look of the book, Growing Object-Oriented Software Guided by Tests.