Test Driven Development. Hot or not?
TL;DR
In this post I share some insights of what is my current understanding of Test Driven Development. Additionally, I review some materials I recently encountered on this matter.
Context
Test Driven Development and Pair Programming were practices that always attracted me. They seem to be pretty neglected in our industry (at least in places I had opportunity to work so far) but I see a lot of benefits coming from them. They are pretty young though (~20 years) - let’s remember, that it took 30 years to convince people about washing hands . So there is still some time left before they become really popular 😉
Recently I was on a workshop where for 8 hours we were coding a Game of Life with focus on Test Driven Development. We paired for 45 minutes with one person and we were using Ping Pong Pairing style . After each session we were drawing some conclusions. After short break we paired with another person and tried to make a session even better.
I had some prior experience with Test Driven Development, however it was very shallow. The only thing that was familiar for me was this diagram:
So you can imagine, that this workshop was kind of enlightment for me. Additionally, instructor recommended us to watch Sandro Mancuso solving a problem with Outside-In TDD . It literally blew my mind and forced me to make some broader research.
Why should I care about TDD?
- It’s a nice workflow.
If you are able to go forward with this Red-Green-Blue flow it can feel almost like a game. However, it’s super hard for me to make a test that forces me to take reasonable sized step. Some tests are red too long (too hard to implement) and some are too obvious - I think that it’s one of the hardest part to overcome.
- It’s helping with design.
If we write a test first - we are optimizing this part of code for testability. And testability is a good thing - usually it’s a trait of good design. It’s easier to spot, that we need to inject some Clock
or IdGenerator
to class under test when writing test first than implementation.
- It’s a way to ensure, that we are solving right problem.
When writing a test we think what exactly do we want to achieve. What is needed to perform some action. What are side effects. How result should look like. What collaborators of unit under test will have. Thanks to starting with such questions we will find answers faster - especially if it turns out that something is imprecise and we need to talk with somebody about the requirements.
Jugglery with Red, Green and Blue caps
Recently I’ve encountered an idea that it’s all about appropriate juggling with three caps.
First, I put on Green Cap. I become a dreamer. In this mode I’m focusing on my wishes. “I wish, that if I make this call that event will be sent”. In this phase we are writing the test that will capture expected behaviour. We focus on the API to be as usable as possible. It should encourage us to invoke it. And it should yell what is it doing. Once we get expected error message (it shouldn’t be “Not implemented yet”. Much better is “Got 0 but expected 1”.) we move to the next step.
We switch to Red Cap. This one is very uncomfortable. We want to remove it from our head as fast as possible. That’s why we don’t care that much about naming, extracting methods etc. We just want to get the shit done the green test.
Once the test is green we switch to Blue Cap. In this mode we are focusing on internal changes without changing behaviour. We should never modify both test and implementation at once as it introduces risk of introducing a bug. It’s a time when we may notice that some name could be better. That some class or method could be extracted etc.
After each green test / successful refactoring it’s worth to make a commit so it’s easy to go back in case we make some mistakes.
It’s good to write pretty small tests to move forward continously. It’s worth to remember that not all tests have to stay in final version. For example if we spot that there are too coupled to implementation - maybe it’s worth to just remove them (if the logic is captured by other test).
In theory, this way of working should fit nice with Ping Pong Pair Programming - pair then should switch caps between each other. The cool effect comes when you have red phase and then the person with which you work can refactor it out.
Although it’s super interesting experience and there are some benefits like knowledge sharing, I did not have opportunity to make it working really well yet. Usually there is a feeling of moving super slow. Maybe I just didn’t try enough?
Classicist vs. Mockist style
It was quite a discovery for me that there are two styles of TDD for me. Usually I thought about mocks as invention of Satan and I hated people who used them. Whenever I saw mock in codebase I worked on I was triggered. I looked for ways to replace it with some fake (for example in memory database). It was because of bad experience I had with mocks in my past. I had a (un)pleasure to work with code to which one change was resulting in 38 tests broken. And developers were going in such case to tests and were finding with appropriate mock settings.
So I only knew the Classicist/Chicago (Inside Out) style - starting from well tested domain that can deliver all behaviours needed and then using this well crafted domain in some interfaces (in most cases REST ofc.). It was so natural for me, that I didn’t see any other options! Then I met the content made by Sandro Mancuso and I must say - I’m really impressed.
Mockist/London (Outside In) style starts with tests that treat our unit as a black box. For example it can hit REST Api and expect that some event is published. Of course - the first thing that comes to mind - integration tests. So it will be slow! But it’s just my distortion caused by years of working with Spring. Of course - integration tests do not have to be slow! One can use libraries like Ratpack , SparkJava and enjoy fast tests.
What I liked the most is that such test is measuring the progress of solution. We think what should first handle the request coming from outside world. Then with use of mocks we design the whole path through all the layers and this test show us what yet is not implemented. For sure - this is the approach I want to build my next application! 😁
It’s worth emphasize that they can (and even should) co-exist with each other as stated HERE and HERE .
It’s just a tool
It’s worth to remember that it’s just a one way of tackling the problem. Sometimes, when we don’t have a good understanding of a codebase it’s worth to try Chaos Development
or Dupa Driven Development
as mentioned by Jakub Nabrdalik in his great talk “Things that work for me so well I cannot believe you are not using it - Jakub Nabrdalik
”.
Awesome Resources on TDD
TDD Thinking Hats - Chris James
I think that idea of wearing only one cap at the moment and focusing on the current task (creating test / implementation / refactoring) comes from Kent Beck, however I like how it’s explained here.
Outside in TDD - Part One - Sandro Mancuso
Solving whole Code Kata in Outside-In style with nice explanations on the way. Perfect way to get the idea “mocks are for designing, not for testing”
Does TDD leads to better design? - Sandro Mancuso
Another excellent talk about London TDD style. It’s like a theory and the previous one is an exercise. Good point made, that TDD does not have to lead a better design - it’s just a tool! Before watching this presentation I was immediately triggered when I saw mock for something different than external dependency (db/client).
Mocking as a design tool - Sandro Mancuso
Basically the same as above, but in form of blog post (if someone prefers reading)
Uncle Bob and Sandro Mancuso on London vs. Chicago TDD
This is an interesting one! I watched only the first part, however I would love to watch the rest as well. It’s pretty expensive though (15$/20$ per episode! To be honest I thought it’s for the whole serie that’s why I paid for the first episode 😅). Anyway, it’s super cool to see such giants interacting with each other and exchanging ideas :)
London TDD vs. Detroit TDD - You’re Missing the Point - Mark Henke
“It depends” answer for which style is best (and as usual IMO it’s the best possible answer). I like author’s explanations for common misunderstandings for both styles. Especially My Behavior Has Too Many Dependencies to Test at Once
and Hey, That's Not a Unit
.
Mocks Aren’t Stubs - Martin Fowler
Similar to previous one but with some code examples and little bit more comprehensive.
Things that work for me so well I cannot believe you are not using it - Jakub Nabrdalik
At 33:33
Jakub talks about many tools for solving problems. It’s worth to remember that TDD is just one technique.
Improving your Test Driven Development in 45 minutes
The way Jakub make use of package
scope in Java is incredible. In Kotlin we can get similar effect with internal
- however we need to split our app for modules then. It’s also super cool view on how big Unit
can be.
TDD for those who don’t need it - Chew Choon Keat
I love the sense of humour of the speaker, but he makes also some good points why it’s worth to practice TDD and how bad TDD practitioners are in convincing others to this practice :)
Extreme Programming Twenty Years Later - Kent Beck
Kent Beck is just a great story teller. It’s nice to reflect how much our industry changed. When I think, that 20 years ago Continous Integration was not that popular, extracting methods was not automated and the more complex code you wrote the better programmer you were… I see how huge progress we made and I’m looking in the future for next great improvements.