Working With Unit TestingNada Ramzy
How long has it been since you had to deal with the frustration of fixing bugs or adding new features to a bad project full of bad code? This week, I began work on one. The thing is, the project is fun and innovative in and of itself, but the code… oh gosh… the code is awful, full of thousand line files, code smells, bad naming and a long, etc. It’s not that I haven’t written code like that in my early years, but I hope you get my point.
I was irritated when I read the code. At first, I was tempted to make the changes as quickly as possible, continuing the previous development team’s careless coding. Then I considered all the things that could go wrong, such as introducing new bugs and endless debugging sessions; remembering that this is a new project for me, and I’m still learning about the main domain rules.
Refactoring is the way to go?
After some thought, I came to the conclusion that I should refactor the code before making any new changes. And I know it’s obvious that there can’t be another way, but I struggled until I accepted it. In any case, a bigger issue emerged: there was not a single unit test in the solution.
Have you read Martin Fowler’s “Refactoring: Improving the Design of Existing Code”? If you haven’t already, I strongly advise you to do so! According to Fowler, you cannot refactor code if there are no tests. Because you wouldn’t be sure you weren’t breaking functionality if you did. And refactoring does not necessarily imply breaking functionality in the process; otherwise, it is mostly deleting and redoing.
So that was my issue: no tests and no way to refactor (at least in the Fowler way). However, Fowler discusses a situation similar to mine in his book, but he recommends reading another book to figure out how to proceed: “Working Effectively with Legacy Code” by Michael Feathers. This book completely changed my perspective on unit tests.
I’ve had the opportunity to learn how to write good unit tests as part of my career. I’ve learned how to identify testing scenarios, test data boundaries, reduce test fragility, decoupling, mocking vs stubbing, TDD (I’m convinced that writing the test first is advantageous for specific reasons), test the tests, and so on. I used this knowledge in Nile Bits and made an effort to share it with my colleagues, and the results were evident. To be honest, there have been times when I simply avoided unit tests, usually due to laziness or a lack of time. But now I’m determined never to push legacy code again.
Stop writing legacy code
Yes, you read that correctly: legacy code. Because Feathers taught me that code that lacks unit tests is considered legacy code. When you think about it, it makes a lot of sense. What exactly do we mean by legacy code? We can say it’s old, badly written code from another programmer (and that programmer could be our past self). But that isn’t the entire picture. Legacy code is code that is difficult to read and understand. More importantly, legacy code is code that you are hesitant to change.
In other words, the moment we commit our shiny new code (without unit tests) to a repository, it becomes legacy code???
Code without tests is bad code. It doesn’t matter how well written it is; it doesn’t matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don’t know if our code is getting better or worse.”Michael C. Feathers
So, how do we keep our code from becoming obsolete? By writing clean code, we do our best to make our code readable for newcomers and to decouple our components with abstractions so they can be changed more easily. That’s all good stuff. But how do we prove that our code is completely free of rot? Have we persuaded ourselves that our concept of clean code is the best for everyone? Is clean code sufficient to ensure that newcomers can confidently introduce new changes?
Unit Tests to the rescue
No, it does not. And this is where unit tests come in. No matter how good we believe our code is, it will not provide future developers or our future selves with peace of mind. Unit tests are the only way to provide confidence to others. And we specifically mention unit tests because they are quick and can be run repeatedly in a matter of seconds (a few minutes at most).
The book Feathers discusses the techniques required to transition from legacy code to good code. It’s not an easy task, but his advice makes it much more bearable. I strongly recommend it to anyone who is in a situation similar to mine and feels overwhelmed at times. It’s greatly assisting me with my new project, and it’s a joy to see how it’s evolving into more robust software day by day.
My new commitment
I feel sorry for anyone who has had the misfortune of working with any of the uncovered code I’ve left behind, because I know how they feel. Programming should be enjoyable work, and I hope to contribute to that goal not only for myself, but also for my colleagues. Let us pledge to never write code without tests again until there is no more legacy code! As if that were even possible???
Are you prepared for your next software project? Our team will turn your concept into a profitable product! Please contact us.
Leave a Reply