I love unit testing and Test-Driven Development (TDD). Ever since I started using them, I felt I became a happier, better, and more productive software developer.
- I love how TDD “forces” me to think about how my code is going to be used
- I love how it helps me think more about my code design and less about how my code is working
- I love the freedom to change my code granted by the tests
- I love to have the ability to easily run a piece of code and get feedback
I want to share this with others.
I believe that if more people use this technique, they will also be happier, more productive developers.
This proved much harder than anticipated.
This post is me trying to share my experience.
In the beginning, there was some lecturing…
Round one: Tell them how it’s done
Years ago, after I started writing more tests and used TDD regularly, I prepared a few presentations to promote different types of test automation, how they are performed, what are the benefits, etc.
People that were on these presentations said they liked them, but they were not practical enough. They did not see anything that will make them try them out.
So telling them was not enough, I needed to show them.
Or even better! They can do it themselves!
Round two: Make them try it out
Done with presentations (for now) I needed another way. This is when I discovered coderetreat. It was a fun event, designed to promote good practices like unit testing, pair programming, and TDD. I participated, helped to organize and facilitate three coderetreats and two other similar events.
Such events are a safe environment used to promote best practices. People can try before using those new skills on “real projects” later.
Did we achieve that goal? Did people actually use jumped on the TDD train and used it in “real projects”?
Many people participated, we all learned a lot and not only about testing and TDD. Collaboration and working in pairs or in a group is essential in these events. I do not have exact numbers, but it is safe to say that “real life” unit testing adoption after coderetreat was low.
There is a retrospective at the end of each event session. People shared their experiences and it was more or less what you’ll expect to hear. We asked about unit testing and TDD and most of them answered that this looked useful. Some were willing to use them, but only when starting new projects. They did not see how can they use it in their existing ones.
After thinking about that I realized a few things.
People came to have fun, learning best practices came second. They came to solve problems like Conway’s Game Of Life and hack stuff. I wanted to make them write unit tests 🙂 Our motivation was different. No wonder they did not use it later. They got what they wanted from the event.
But an even bigger problem was it was not enough. They learned to write unit tests, but this was not enough to test more complex code.
Looks like we needed to overcome another obstacle.
Round three: Unit testing is not enough
Not everybody has the luxury to start anew. To test older projects, people needed more skills:
– skill to test more complex, already existing code
– skill to refactor code, so testing is easier
We need to develop both skills. Which one is more important?
Many people will be afraid to or cannot refactor and focus on learning to test “better”.
What do I believe? The code should be simple, so I focused on making the existing code testable. It will not only make you test your code, it will make your code better as well.
Another exercise, focused on making existing code more testable, was needed.
Several months later we made another event. It was not a coderetreat. We did not solve something from scratch. The objective was to take already existing code and make it testable.
For code, I took I the Emily Blanche refactoring katas. Check them out if you are interested. They are many of them 🙂 They are also specifically designed for such events.
The results were interesting.
People had to write test code, but this was near impossible. They had to change “production” code, there was no other way. The interesting part was how much they resisted refactoring. Many will make everything possible to introduce absolute minimal change. This sounds logical, but the code will still be super hard to test after. They were not willing to go all the way.
Let me give you an example.
In one of the exercises, a pair had to test code containing a random generator. This code was near impossible to test. They got no access to the generator so they had no idea what value it will generate at all, but still had to account for that in their tests.
How did they fix the problem? They run the code under test many times until the generator will produce a value they wanted to test. Then they will proceed and assert if the behavior they needed was OK.
Yes, they tested the code, but that was not the objective at all. What if you want to test a very rare event? One that is one in 10000 times? Would you run your test 10000 times until your random generator finally exactly returns the value you want?
Other people correctly saw the exercise purpose and refactored well, so they can make unit testing easier. But less than expected.
I guess this is why a facilitator was needed, so I steered where I could.
Still, what made them so afraid of changing “production” code?
It was a simple made-up project from Github! What did they have to lose?
Is “If it is not broken, do not fix it” that strong?
Why it is so hard?
Unit testing is not introduced early
Open a tutorial for a computer language or framework. Plenty of hello worlds there, but not a lot of testing, isn’t it?
There is the point of “why make people learn one more thing at the start”. I do not agree with that point. With some guidance, tests can really help even a novice. They are not that hard to teach on a trivial code when you teach something new.
You need to introduce it early because if you do not, people will never program with that in mind. Even now, after years of writing tests, I still notice habits in the way I program that make it hard for me to test code.
We need to introduce testing earlier, before habits that prevent it pile up.
It requires more skills than you think
As a summary of all of the above, to successfully adopt unit testing/TDD/other test automation techniques, you need to:
- Learn how to write unit tests and automated tests in general
- Learn what testable codebase and architecture looks like
- Learn to refactor
And this is just the technical skills!
So for successful adoption, you need to invest in people skills maintaining and refactoring legacy code – something that requires new architectural knowledge and the ability to write more complex unit tests.
This is all a big mountain to climb, so plan and have some patience 🙂
Thank you for reading!