I started learning programming in high school and I immediately fell in love with it. I loved to solve programming problems and challenge myself to improve my solutions. Then I enrolled in a Computer Science related Bachelor’s degree and got my first internship at a startup as a Software Engineer. With academic and professional experience I started consolidating my programming knowledge and improving my skills. Back then my programming process was more or less like this:
This process can also be represented in a habit loop:
I haven't learned to write automated tests such as unit or integration tests in the university. Besides that, in my first two professional experiences I also didn’t write any automated test. Therefore, I got pretty used to the process in which I was rewarded with the pleasure of knowing the problem was solved after executing my program and checking that it was working through manual tests.
Then, I became a QuintoAndar employee which is a company that has a strong software testing culture. At QuintoAndar the engineers should always write tests for their code. So I had to learn not just to write automated tests but also how to add them to my programming process. My first attempt to change my coding routine looked like that:
The new version of the process was much longer, and I often spent much more time trying to make the tests work than coding the implementation. This was happening because many times I didn't write a code that was easy to test, therefore I had to rewrite it or create complex testing structures/algorithms.
However, the biggest problem was not the size of the process, but the feeling of frustration I felt when I was testing. This was happening for two main reasons:
- Since I was only writing the automated tests after asserting the implementation code was working through manual tests, I felt I needed to write them just to prove that.
- I was really used to my previous habit loop, so once I fished the manual tests I was expecting the reward of knowing the code was ready. Instead, I still needed to write all the unit/integration tests and make them work.
Now I understand the cause of the problem. I used to though that “coding the implementation” and “testing the code” were two separate processes. And I would only start the second after finishing the first.
Then, I knew I had to change my relationship with software testing in order to stop getting frustrated every time I had to write automated tests. That's when I started studying more about the subject and came across TDD (Test-driven Development). I already knew TDD existed, but I didn’t know much about the approach. When I started to apply it, not only I improved my productivity, but I also started developing higher quality software
TDD can be applied in different ways, but the main idea is that the automated tests are written before the implementation code. They should reflect the requirements of the problem you are trying to solve. Therefore, every rule or expected behavior should be tested. Here are 3 simple steps to accomplish that with TDD:
- First, write unit/integration tests or any other kind of functional test script that will assert that your code will work as expected based on the requirements. It is a good practice to run all the tests before writing the implementation to make sure they all fail (this prevents false positives). — This is known as the Red phase;
- Write the implementation code to pass the tests. — This is known as the Green phase.
- If necessary, refactor the implementation code to improve it without changing its behavior. — This is known as the Refactor phase
TDD has many benefits, but, for me, the best one is that you have to think about how you are going to implement the code before doing it. Planning all the classes, methods and data structures that will be used is essential, since writing the tests for them comes before the actual implementation.
That’s what makes all the difference. When planning, it is necessary to think about how you are going to code and to test simultaneously. Therefore, coding the implementation and testing it start to be two things that evolve together and are entirely related. They stop being two separate processes that are going to be done one after another.
Finally, my current programming process is like this:
Yes, this process is still longer than the first one, but now I develop much better software and I can do it even faster! Before I started planning my implementation, I often started coding and suddenly realized that my code was not going to work, so I had to change my approach and rewrite the code. Also, I often caught bugs during the manual tests which were done only after the whole implementation was ready. Now, instead of that, I can execute the automated tests at any time of the development, so it is possible to catch the bugs sooner and fix them faster.
The frustration I was experiencing was gone because the automated tests started being a tool that helped me to be more confident that my code was meeting the requirements even before executing it.
If you have never applied TDD give it a try, it may also change your relationship with software testing!
Thanks for reading! I’m always open to receive feedback, recommendations, or questions, feel free to contact me!!
Clarice Abreu, Software Engineering @ QuintoAndar
If you’d like to be a part of and help shape awesome processes like the ones described here, join us!