At the beginning of my career as a Ruby developer, I used to work on a project that had barely any tests. This, combined with poor code quality, made the development of any new feature similar to a walk through a minefield — you never knew if your change is going to crash the app, even in a different place. Add my lack of knowledge at the time and a lot of stress, and you get a nightmare of experience for any beginner programmer.
Honestly, it was a miserable time. The project left such a vivid memory that it serves as an ongoing warning to avoid similar ones at all costs.
It’s been a while, and, luckily, I had a chance to learn proper practices, among them — Test-Driven Development. I can’t recall how many times it saved me from coding struggles in the course of my career.
Hence, knowing both sides of the coin, I want others to be able to kick-start their programming journey instead of making it a struggle.
Let me explain what’s the general idea behind TDD and give you a couple of reasons to give it a shot in your software development process. So, without further ado, let’s get started.
Test-Driven Development (or TDD) is a technique of software development in which you turn the requirements for a specific piece of code to a set of tests. Then, you write the code that should meet the requirements and make the tests pass. When you’re satisfied with the outcome, pick a new feature, and start all over. This perpetual process will result in a test suite created along with your main codebase.
Sounds easy, but why should you add an extra layer of code to maintain? There are plenty of reasons to do so:
When you plan a feature, it’s worth giving extra attention to good tests. There are plenty of things to consider when creating a new feature, among them performance, readability, etc. Still, no matter how your code is going to perform, you need to have it working correctly.
Tests put your focus on the expected results. After you prepare them, you can work on providing a piece of code that will meet the requirements.
Once you have it figured out, you can focus on code augmentation in a way that will let you achieve the same outcome, but in a better way. And if you ever get lost, there is a checkpoint you can always roll back to and start anew with a working piece of code.
In the early days of the project, it’s tempting to forfeit automated tests. I mean — why would you if you can grasp the whole concept? But what if the project grows and it becomes harder to remember every single bit of it?
Think of writing tests as an investment — you need to sacrifice some time now to save a lot of it in the future. If you forget about the tests at the very beginning, you will waste time and nerves while manually checking if everything works fine. And the bigger the app is, the worse this problem will become. On the other hand, a good test suite gives you the ability to check if the new changes are compatible with the rest of the codebase.
Moreover, it will save you plenty of time. Instead of clicking through the whole app you will run the test suite and wait for the results. If there’s an error, simply fix the code and you’re good to go.
Your code might behave in many different ways depending on the conditions. Good tests dispel all doubts by providing a clear description of the code’s behavior.
If you ever forget how a class works, you can come back to the test suite and check it. Moreover, it helps in the onboarding of new team members by being a single source of truth about the code.
I find this especially prominent when using Behavior-Driven Development frameworks like RSpec. To me, such tests have much more expressive syntax than regular ones.
I like the verbosity of tests created with BDD frameworks. It resembles natural language closer, hence making it easier to read even if you don’t know the programming language well. Still, either choice is fine, as long as you care for the quality of the tests. Just remember that better tests equal better documentation.
You can easily set up automated tools that help you keep good code quality by running tests. You can do it on various levels.
One common practice is to set up Continuous Integration (CI) tools. CI is a development practice in which developers frequently integrate their code into a shared repository. It intends to make the app as up-to-date as possible. Such an approach requires quality checks. Running tests on a dedicated server is one way to accomplish that. There are plenty of ready-made solutions in the market like TravisCI and CircleCI. You can also set up Jenkins on your server to run the tests for you.
And if that’s not enough, there’s plenty of tools you can run on your machine to monitor changes in your files and run appropriate tests. A couple of examples would be Guard for Ruby or Jest tests, where you append
— watch flag to
npm run test command.
I hope that I managed to show you the major benefits of using TDD. From my observations, it isn’t only another way to make your code better. Rather, it has become an industry standard and it happened for a reason. If you’re striving to make yourself a reliable developer, this is a way to go. Also, if you’re working on a project alone and have nobody else to check your code, or maybe have a worse day, automated tests can save you a lot of fuss by catching things you would have missed otherwise.