I go to the gym and lift weights because I like the feeling of getting stronger and better. Two months ago, I started to feel a throbbing pain in my knees, yet I continued to go to the gym.
I wanted so badly to keep getting stronger that I neglected my health and pushed myself to keep lifting weights. As you can imagine, my knee pain didn’t improve, and more and more of my body needed to recover after each grueling workout session.
As a junior developer at Rackspace (as part of the Airbrake team), at times I feel a similar drive to continue pushing out code that I think will make the product stronger. But just like my knees would complain each time I squatted, untested legacy features would buckle and sometimes break under the weight of my new features.
Eventually my physical therapist convinced me to focus on rehabilitation and prevention before continuing to lift weights. When I was healthy and had more stabilizing muscle strength, he said, I could progress faster and less painfully because I’d be less likely to hurt myself.
Taking his advice to heart, I saw a connection to my job. I felt obliged to write tests for legacy features in my product so I could progress quickly and have more confidence that I wasn’t destroying any other features along the way.
Here are some key takeaways I learned during that process:
Tools are important
I use RSpec, Capybara, FactoryGirl and Selenium for testing my Rails app. Before you write tests, be sure you research the right tools. You don’t want to spend more time wrestling with new tests than fixing broken code.
Even simple tasks like cleaning your database before and after the tests run are important to get right if you want to keep tests fast and repeatable. To do this, I use the database_cleaner gem.
If you’re the first developer on your team to catch the testing bug, it’s important to also consider that tools are important because a poor choice here can lead to others on your team neglecting to test. The whole team has to commit to testing, which means you have to make it as easy as possible to begin testing, starting with powerful, flexible tools.
Specs tell you how code should work and not how it does work
When I began testing other people’s code, I noticed I was getting in the habit of writing tests that passed first and then assessing whether the code was actually supposed to work that way. Sometimes it worked, other times I ended up writing tests that passed only because I was overfitting to my examples.
It can be tempting to take the easy way out and assume all up-to-date code works as desired and then write tests to confirm that assumption. This can lead to a strange pattern of reverse-test-driven-development: the code becomes the spec for the tests.
Focusing on the original intent of the code is a safer option, and senior developers will most likely be willing to clarify code if you can’t figure it out on your own.
You can’t test it all, so don’t try
Writing tests for an entire production app can be a full-time job. Of course, we all want 100%(+) test coverage, just like we want to have a sink free of dirty dishes. But instead of arduously cleaning the whole sink once in a while, another alternative is cleaning and putting away your dishes away as you use them. I took this strategy and now write tests to go with any new code and any legacy code I may have touched in the process.
I recommend testing as a useful procrastination for junior developers, since it forces you to learn about the code base and gets good muscle memory ingrained. But do make sure you follow these guidelines when you test.
PS: My knee feels great, thanks for asking.