I have always considered automated testing as part of the must-have best practices in software engineering. The sole purpose of writing tests is to make engineers feel confident before shipping code. "Use as much machine power as possible, do not waste brain power" - is my favourite quote on testing from the most brilliant guy I have met. And I would like to jot down my thoughts on why it happens to many codebases that tests are not well written or even no tests at all. Yet, I think there is a way to fix it if we have a will. Stay tuned and read on guys 🥳
First and foremost, let's highlight two focusing points in this post:
7 years ago, I started fresh in software engineering. I did not know anything about testing at all. I am a full-time self-taught developer, nobody told me about testing before that. I started working for a medium size company where no single line of tests has ever been written except automation testing team - aka Quality Assurance (QA). Everyone just wrote code, had it run on their favourite browsers, and clicked around to see whether their tasks were achieved or not. Well, I thought that was it - no testing. Yet, one thing that bothered me a lot was the number of bugs we kept receiving weekly whenever we get into the bug review section. I knew something was deadly wrong but nobody could explain it to me. Most people in the room just said "Software has bugs, so we fix them". Until today, I still completely agree with that with a major difference - how to avoid the same bugs occurring over time - aka regression.
A few months after I kick-offed my career, I was extremely lucky as I was picked up by a colleague at work who is a friend of the most talented software engineer I have ever met. They wanted me to join a side project of him. The guy was one out of five Google Developer Experts in my country. In my first meeting with him, I was super nervous as I never met anyone that smart in person. Yet, I was eager to learn from him - I even studied before the day I met him 😂 I was able to qualify for most of his requirements to join the project except one - its testing. I had no idea what he was talking about. Then, he spent two hours of explanation and demonstrating the way he tests everything he has ever written. That's exactly what we needed at work!
Software is designed to solve human problems. The problems we have will never be a set of fixed problems, yet rather change as time goes on. Hence, the bar of meeting business requirements and code quality at scale is hard. Data Structure & Algorithm, SOLID and Design Patterns are must-have for every successful codebase I have ever encountered. Even with these in our tool belt, it is still tremendously hard to get things right in the first place. I do not know any software that does not have bugs, lack of capacity, or further potential improvements. To error is human, we will make mistakes for sure. The key difference between novice and professional engineers is how they tackle those mistakes. Refactoring is one of the best ways for increasing code quality at the codebase regardless of its scale. Yet, even before refactoring can happen, we need tests!
Before you start refactoring, make sure you have a solid suite of tests. These
tests must be self-checking.
The statement above is quoted from one of my favourite books in programming written by the legend Martin Fowler. Refactoring - Improving the Design of Existing Code. I have never seen any successful software libraries without tests - take React, Vue, @Tanskstack/query, Solid, et cetera. It's even a part of their code conduct of contribution to passing the existing tests and/or having test coverage on changes. I often found myself spending triple the time on testing code versus developing code. It's simply because the tests make sure all my requirements will be automatically met, the number never lies unless you tell a machine to do so 😅. Take dataloader.js library for instance, the code itself is only 500 LOCs, yet the LOCs for tests are more than double.
Below is my favourite talk about What, How, Why by Simon Sinek - he is a great influencer. The talk explains the difference among those three and highlights the importance of WHY which is often neglected by many of us. Thus, I believe the concept is universally applicable to anything - testing in this case
Let's start with the most important one - WHY. It is not uncommon that I have seen developers try to write tests just to cover their code without being able to reason about it. There are sample tests written previously and they simply copy + paste, then modify some variable names to fit their use cases. It feels so reluctant that way. If you can't give meaning and love what you do, it's almost impossible to deliver a great job. You already fail before you even start. The talk below explains exactly that - highly recommended to watch it
The important lesson I learned from many talented engineers about testing is that. It supposes to give us confidence, nothing more. It's exactly that. Why does someone even bothers to write a test in the first place (Test Driven Development - TDD), then write the code to make them pass? By doing that, they feel 100% confident that they are doing exactly what is necessary to achieve their goals. They instruct the machine to do the work by leveraging their brain power - and the machine is designed to do precisely that. It not only does protect your sleep at night while your code running in all kinds of environments but also supports other developers to contribute code at scale. Unless the submitted code is well-covered by test cases, they should not be able to merge code to the shared branches such as dev / master / main / canary / etc. Hence, confidence gives our writing tests a lot of meaningful purposes which in turn makes us love what we do.
Secondly, it is the easiest one in my opinion - WHAT. Very often, before we start writing any line of code, we already know WHAT - it's the problems we try to solve. If it's an application, it's your business requirement. If it's a reusable library, you intend to help other developers achieve their tasks
Last but never least, it's HOW. This is where I struggled the most in the past because testing tools are not available much for UI engineers like me. Thanks to the huge success of the web community, we had healthy competition and the winning tools benefit us all. It has become much easier to write tests nowadays and runtime is so fast. There are many kinds of tests such as unit tests, smoke tests, end-to-end tests, static typing analysis, performance tests, stress tests, visual tests, et cetera. It depends on your project's standard to focus on those differently. Yet, I found myself involved in integration tests mostly and end-to-end only when integration tests hit its limitation and the most crucial ones such as authentication and payment flows. However, testing practice varies among people. We don't have such a thing like SOLID and Design Pattern to follow in testing. Yet, our feeling is never wrong. Whatever practice could make you feel so good given the same effort? You should stick to it. Over the years, I have seen several pitfalls as below:
The two practices I have followed for years and haven't seen it's going anywhere are from these two legendaries:
And this is my borrowing testing trophy for life made by Kent
Hopefully, by reading until this point, you guys could agree with me more or less on the importance of testing. I'm happy to help with whatever questions you have. Please feel free to contact me for further concerns. Testing is great and everyone should learn it. Surely, it is hard, takes time and patience to develop the right practice. Yet, the right way is usually the hard way. Happy coding ❤️🔥
❤️❤️❤️ Be well, Be happy ❤️❤️❤️