Your Mission… Impossible!
I’m sure you’ve heard of this recommendation:
Whenever you fix a bug, add a test before. [stackexchange.com]
Unfortunately, when working in legacy code, this is more depressing than empowering. I’ve seen codebases in which adding a unit test can take weeks! Spending 3 weeks to add a test when fixing the bug will take 1 hour is often unjustifiable. It’s not easy to swallow for product managers or customers.
This situation can get pretty stressful. Worst of all, it can only get worse!
The False Promise: Mocks
When testing legacy code, most of the time is usually spent with the data setup. Legacy code is often a tangle of dependent classes. For every object we need to instantiate, we’ll have 5 others to set up before.
The default solution to this problem is to use mocks to shortcut all this data setup.
- It gets the test running in a reasonable time
- Every test will have its own mocks of specific classes. Unfortunately, legacy code mocks duplicate the behavior of the real system. After a while, this creates rigidity. It will backfire and make refactoring more difficult. The exact opposite of what we wanted! That’s what I call Mock Hell.
- The flip side is that these tests won’t be very robust. When we have to refactor the code, we’ll have to refactor the tests. Worse, if the real code behavior changes, tests will silently continue to pass!
- Finally, we often need to refactor the code before we add a mock.
Mocks have serious downsides. As I wrote before, using mocks to speed up test data setup is an anti-pattern.
Mikado to the rescue
The mikado method is the perfect workaround against long technical endeavors. It lets you split them down in small steps that we can deliver along with other business-as-usual tasks.
Let’s update the initial recommendation:
💡 Whenever you fix a bug, start a mikado graph to add a test around it.
You can now write your test at your own sustainable pace. It might take 1 month, but it’s not an issue because you are not blocking features. There is no tunnel effect. You won’t need to use mocks.
What does it mean to start a mikado graph?
You want to make the plan and the progress visible for everyone in the team.
If you are all in the same room, stick it on the wall! If you’re working as a mob, remind it to your buddies.
Otherwise, you’ll need valid working agreements! For example, we used to generate mikado graphs from Jira tickets. Your team will have to find its own ways. Retrospectives help there!
The Test Data Builder Pattern to make it Viral
The flip side of using the mikado method out of the box is that many tests will end up setting up similar data. The typical fix to this problem is the test data builder pattern. This is where it becomes magic.
Let’s update the initial recommendation again:
💡 Whenever you fix a bug, start a mikado graph of test data builders to add a test around the bug.
Most nodes of the mikado graph will be about writing a test data builder for some data of the system. Every time you finish a step, you’ll have a new reusable test data builder.
At every step, you increase the testability of the system. As other developers try to test their own code, they are likely to:
- Re-use these new builders to add their tests faster.
- Improve the builders along the way.
- Make others even more likely to re-use these builders!
Try it yourself
It’s between 2 and 4 hours long. For the moment, code is available in Java and C++.