This article was originally published in Typeform's Engineering Blog
I’ve been hearing about the importance of writing tests for years. No wonder, since during all that time I was working as a QA Engineer within a team of developers. I’ve heard about how important unit and integration testing is, the wonders of TDD, code coverage, you name it.
Agile teams and organisations kept talking about it, and for a long time I assumed it wasn’t related to my job. Even though it had the word “test” in it, it wasn’t the kind of testing I was involved with. So I just wrote it down in my mental “Book of Things That Are Good Things” and hoped that one day I’d grasp the reasons why it belonged there. I was clueless.
In the meantime, all those years ago, I was learning to code. I wanted to switch my career to development, or at least test automation. Sites like Codecademy were just getting started back then and I was going crazy with their free courses. Somehow it never caught my attention how the exercises knew when my code was incorrect, how come they usually had a pretty good idea of what part of it was off target.
After that, I did some more intense “classroom coding-courses”. There, I learned a little bit about the purpose of unit tests. They test every functional cell (or unit) of the code to make sure it does all that it is suppose to. Now, after letting this new definition sit in my head for years without giving it a second thought, I realised something new:
Unit tests don’t test outward facing features –like a login form or an endpoint– they test the inward facing functionality, making sure a piece of code can be properly used by other pieces of code.
Since I was learning some new tricks I was also starting to create small pet projects, either to apply what I learned or to challenge it by building something fun that I came up with. There were a lot of tedious sides to this: obsessively googling for solutions, not using git, figuring out what CORS is… you know how it is. But one of the worst things about my process (or lack thereof) was having to test every single use and edge case on every single change that I made.
If you’ve read this far, you probably know what I’m talking about. Either you’re experiencing it right now, or you remember it from when you were starting to develop real, working things. There’s no way of forgetting that tedious feeling of having to manually test every single flow variant each time you introduced a change in the logic. Or, worse, discovering a broken flow after a 3-hour-long refactor and not having a clue of when it had stopped working.
Now imagine a situation where all of that is covered by just running a command on the terminal, and watching a sea of green (and occasionally red) text unfold. Wouldn’t that be lovely? It definitely would, but somehow it still escaped my understanding. My projects are not “real applications”, I thought, they don’t need tests.
I’ve been working at Typeform for a about 6 months now. I’m part of a team that is very self-sufficient testing-wise and they don’t really need me to keep the QA hat on at all times. We do a lot of mob and pair programming and I get to take part in the development of features, changes, and fixes. I’m learning a lot with this experiences, but there’s one thing that always surprises me: when we finish a task, my teammates immediately want to write tests for it. It feels almost as if it were an impulse or an unconscious reflex, not something they remember they have to do. And somehow they always catch me off guard.
At those moments, I’m inclined to feel happy and relaxed. The fact is we’re done understanding the problem, discussing approaches and sorting obstacles, and finally got the thing working. I just wanna take a quick coffee break, but all they want to do is make sure everything is covered with tests.
And it’s only today, after almost 9 years working in the software industry, that I understood why:
These people care about code, and not only the code they write themselves. They care that it’s readable and maintainable, that it follows good standards, that it’s efficient and that it does everything it’s supposed to do, both inwards and outwards.
But, they work on real-world codebases, which tend to have a lot of code, in a lot of files, for a lot of features. And there’s only 24 hours in a day. They need a reliable and fast way to ensure that their change works and — most importantly — that everything else still works too.
Great developers love writing tests because they care about the code, and they know that (well-written) tests are the only reliable way they can feel more confident that it works.
They also know that if it doesn’t get tested when it’s fresh out of the dev oven, it probably never will. This is also why things like Test Driven Development (TDD) exist.
When you use a TDD approach, you start by writing tests for everything that you need to add or change. Yes, I’m serious. Write the test before you write the actual code. All the new tests will fail, of course, but then you write your code so that they all pass. You start by having coverage, then build the thing that you need to cover.
TDD has an additional value: focus. Only code that makes the tests go green should get written.
My final point here is not to brag about getting to work side-by-side with great devs, but rather encouraging you to get acquainted with testing your own code sooner rather than later. Make it a necessary thing in your process. Not because it’s an industry standard, a good practice, or because somebody said you had to. Simply because it will no doubt make your life as a developer easier and happier.
An please don’t be like me, some things are too obvious to take years to put together 😉