On my last three production web applications, before I wrote the first line of code, I set up the test framework and added a code coverage tool that throws an error if code coverage is not 100%. I then setup a simple continuous delivery pipeline using GitHub, CircleCi and Heroku. Every check-in to master is automatically tested and pushed to Heroku.
From day one, this setup forced our team to create automated tests for every line of code we write. These projects have never had code in them that is not executed at least once in automated testing. 100% code coverage has helped us build confidence in our ability to deploy new code often, sometimes multiple times a day. More importantly, it has allowed us to deliver more functionality, more quickly. It’s the only way I want to work now.
The most important purpose of automated testing is to give us confidence. We want to have confidence that we can add new features quickly. Confidence that when we add a new feature, fix a bug, or refactor a module that we won’t break other functionality. Confidence that when others work in the code, they won’t break the existing features. Confidence that the project can stay in this state in perpetuity, always ready to ship, always ready to add the next feature, always ready to be improved.
Tests allow us to move faster. Some might argue that writing test slows you down. If all you measure is how fast one developer can get a feature out then yes, that’s true. But if you measure how fast multiple people can add new features to a product over months and years as the product grows and evolves, all while keeping quality high and not breaking production, automated tests allow you to move so much faster.
100% is Bare Minimum?
Some consider 100% crazy and a waste of time. I consider 100% the bare minimum. I actually want tests to be more than 100%, meaning that tests should execute parts of the code over and over again for different scenarios and configurations.
There’s something magical about 100%. It clearly states to anyone coming into the project that you simply cannot work in this project if you don’t write tests. I worked on a project where we set the limit to 95% early on in the project. That left this little bit of fudge factor. There was always room for an exception. And where there’s wriggle room, people will wriggle. So little bits of untested code kept getting into the project. Requiring 100% removes the exceptions and excuses.
I know there are a lot of objections out there.
- “I like getting my features done fast” — I get it. You get great personal satisfaction out of getting that feature done as quickly as possible and seeing it live on the site right now. We all like to be the dev who pulls off the “miracle.” Consider changing your personal reward system. Instead of focusing only on speed and wow factor, take pride in delivering easy to read, easy to maintain, high-quality code that has automated tests around it that will keep that feature easy to enhance and always functional. Take pride in delivering features at a consistent pace. Features that just work. Features that don’t require rework. Features that continue to work for the life of the product.
- “We have to move fast right now, we can do the tests later” — Later just never comes. If you don’t have time now, why will you have time later? Even on that 95% project I mentioned earlier, we always talked about getting to 100% and staying there, but it was always put off until later. Just start at 100% and stay there.
- “My boss/client doesn’t want to pay for automated tests” — Your boss or client is going to pay for testing one way or another. If they don’t pay for automated tests up front, then they will pay the ongoing cost of manual testing and the increasing slowness and unreliability in the product. I’d suggest that you don’t even give your boss or client the choice. Automated tests are just part of your estimates and part of how you deliver software. Don’t even raise it as a question or option. Just do it. They’ll thank you 6 months from now when the code is clean, easy to maintain, easy to deploy and fully tested and your growing team is able to move at an increasing pace while maintaining high quality and reliability.
- “We already have a large code base with few tests” — Will this change be challenging? Absolutely. It will take time and patience, but it can be done. Set up your test framework and start measuring code coverage. If you’re at 10%, then set a goal to get to 20% and then move on from there. Don’t make a big deal about this with management. Don’t try to stop the project and do nothing but add tests for a month. That is rarely acceptable. You have to figure out how to continue adding features (with tests now) while slowly adding tests for the existing code. You have to figure how to increase your test coverage while you’re still adding features. The alternative is to continue to have a code base that is hard to improve, slow to work in, and hard to ship.
- “Code coverage numbers can be inaccurate” — Depending on your language and tools, code coverage may be inaccurate. And just because your tests have executed every line of code doesn’t mean that they’ve tested every possible case. Absolutely. I agree. That’s why I’m arguing that 100% is the bare minimum. It’s just the starting point.
- “I want to do this, but my team doesn’t” — That’s a tough one. This is kind of an all-or-nothing thing. You can write tests for the code you write, but if no one else does, you won’t have confidence that you’re not breaking anything else in the project when you make changes. Even if you write great tests for your code, when others work in it, they won’t maintain the tests. That said, write the tests anyway. It’s still the best way to show the benefits and help make the argument for everyone doing it.
There’s a whole lot more to code quality than code coverage. You still need to write secure, performant, easy to maintain, high-quality code. I’m not suggesting 100% code coverage is the end of your quality efforts, but I am suggesting that it be the starting point. Give it a try on your next project. See if it works for your team.