Distillation of “TDD, Where Did It All Go Wrong”

Not long ago, UncleBob promoted the conference talk “DevTernity 2017: Ian Cooper – TDD, Where Did It All Go Wrong“.

I’ve watched the talk, took some notes and here’s my distillation of it.

51kDbV+N65LIt seems like the development community lost track of what TDD is at its core. It seems we tend to opinionate a lot about concepts, patterns, methodologies, but we only know them by what we heard other people say about it… And like in a game of Chinese whispers, the original idea ends up distorted. The problem is that too many of us in the community tend to not read the books that originally published the idea.

Therefore, Ian Cooper advises us to read the original book that published the idea of TDD: “Test Driven Development: By Example” by Kent Beck, published back in 2002.

Despite risking being part of the game of Chinese whispers, Ian Cooper tells us about all the mistakes he made regarding TDD, since he started doing it back in 2004, and tells us how he should actually have done it according to Kent Becks’ book.

The gist of the problems that Ian Cooper sees with TDD is that many developers, even very experienced ones, don’t really want to do it. This is because:

  • tests are slow to write;
  • they “need” to test every little thing;
  • you “need” to write tests for every piece of code;
  • when we change the implementation we also need to change the tests;
  • acceptance tests (which simulate a user clicking on a screen), are slow and too unstable because they need to change whenever the UI changes;
  • they are slow to run;
  • in sum, tests are annoying because they make us slow and none of us wants to be slow, we want to be the guy that quickly delivers reliable functionality!

I would also add that these tests end up only making sure that changing a piece of code doesn’t break another unrelated piece of code, although that is not the goal of the tests, that is something the codebase should do on its own by means of module isolation.

However, this is not how it should be! This is not how Kent Beck envisioned it, and Ian Cooper reminds us of that. Heres my distillation of Ian Coopers’ bits of advice:

  1. Refactoring is the process of changing an implementation while maintaining the same behaviour;
  2. One of the promises of unit tests is that, when refactoring an implementation, we can use unit tests to make sure the implementation still yields the expected results;
  3. Focusing on testing methods or classes creates tests that are hard to maintain because we will need to update them when we refactor the implementation of the behaviour. Furthermore, they don’t test the behaviour we want to preserve, otherwise, we wouldn’t need to update them when we refactor the implementation;
  4. Avoid testing implementation details, test behaviours;
  5. The trigger to add a new test is not the creation of a new method, nor the creation of a new class. The trigger is implementing a new requirement;
  6. The Unit under test is not a class nor a method, it is a module. A class may be the entire module, or a class may be the module facade, but many classes are just implementation details of a module;
  7. Test the stable public API of a module;
  8. Write tests to cover the use cases or stories;
  9. Do not test using Acceptance TDD, use developer tests using the implementation language, they are faster and more stable;
  10. Use the “Given When Then” model;
  11. The unit of isolation is not the class under test, but the tests themselves. Tests need to run isolated from each other, but they can and should test several classes working together if that is what is needed to test the behaviour.
  12. Avoid mocks at all costs, use them only to isolate the tests on the module boundaries;
  13. We avoid the file system and the database, to help isolate tests and make tests faster. Those are, most of the time, module boundaries.

In the end, the message is:

Test module behaviours, not implementations!

Advertisement

4 thoughts on “Distillation of “TDD, Where Did It All Go Wrong”

  1. Hey Herberto, nice post. I watched the full talk and that got me really interested in the book. It also reminded me how much I like to learn and improve myself as a developer. Unfortunately I have not been lucky with my past companies as they do not offer me ways of growing, like the company I first landed when I arrived here in the Netherlands. I think I have been putting the responsibility to grow externally when it is my responsibility and not the companies.

    Certainly will read the book, as I am on a reading spree this year, just not technical reading. Well that is changing, and gonna allocate time to put in practice that knowlege at home if the company does not allow me to do that.

    Thanks for reviving my curiosity again and keep the posts coming 🙂

    Abraço,
    Fernando.

    Like

  2. Hi Herberto. This is a great blog post! I’ve read the book, but haven’t watched the talk.Yet. However, it is true that developers don’t follow the process described in the book. One other thing devs often overlook is that Kent Beck always started with the question “How am I going to test this feature?” instead of “How am I going to implement this feature?”. And, as you say, during the book I don’t recall him ever saying that the “unit” was the class or the method. Instead, he had a bullet list of requirements in a text file that he used to guide his progress. Another interesting thing is that he wasn’t afraid to delete tests! If, when refactoring the code and the tests, the same code path was already being tested elsewhere, he just deleted it! This is something that devs don’t do, but that can really ease test code maintenance!

    On another related issue, I remember talking to a workmate that, back when we were trying to follow DDD and Clean Architecture, about something similar, but from a different perspective. I’m not sure whether he has read this book or not, but his vision was that we should only test the application services, because that’s where the use cases are driven by. In a sense, this is their “stable API”, I suppose. 🙂 This approach lead him to interesting situations. For instance, there was a situation where he was certain that he had covered all the use cases with all its scenarios. Then he ran the code with coverage and there was a bit of code logic that wasn’t covered. The code was analysed and the way he increase code coverage was by deleting that piece of logic. It makes sense! It wasn’t used! If we drive our tests by the feature or use case, we can achieve this kind of vision over the code. When we test method by method the unit tests become very abstract and very technical (meaning that the tests will mention things like “exception”, “data type A”, “Max allowed Int” more frequently then they mention business terms) which makes it really hard to detect situations like these.

    To be fair, I’ve never used this approach myself and never tried to convince anyone in my current project. Mainly because I’m always afraid of having domain entities with very complex business logic that would be very hard to test. But maybe I’m wrong and this is the way to go. Maybe, if the domain entity is too complex, we’re doing something wrong. I don’t know 🙂 But, in one hand, I like to know that there are blog posts and ideas that sound similar to this approach that I’ve cherishing for so long but haven’t yet tried.

    Like

  3. Hi @hgraca. Yeah I also saw this talk by Ian Cooper some days ago. I think all the bad things he says about tests and why most of us developers hate them is the the reason that made Dan North create BDD. Testing behaviour with GWT scenarios. And using hexagonal architecture to test the app in isolation from real world devices. That’s what I do and it’s great. Regards, Juan.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s