Most programmers do not write tests. We all know that we should write them, but for whatever reason, most of us don't. This is unfortunate, because testing is the most powerful tool we know of to improve software quality. Tests reduce bugs, provide accurate documentation, and improve design.
In our consulting work we hear every excuse imaginable for not writing tests. For example:
· "I don't know how to write tests."
· "Writing tests is too hard."
· "I don't have enough time to write tests."
· "Testing is not my job."
· "My code is too simple for tests."
· Blah, Blah, Blah ...
We hope that by writing this article we can dispel these myths and encourage programmers to do the right thing and write tests!
Tests Reduce Bugs in New Features
We advocate writing tests as you write new code. Tests do not eliminate bugs, but they dramatically reduce the number of bugs as you add new features.
Tests Reduce Bugs in Existing Features
With well-tested code, introducing new features rarely breaks existing functionality. If a new feature breaks existing functionality, existing tests fail immediately, allowing you to pinpoint the problem and fix it. Without the tests, you may introduce a bug that is not found for days or weeks.
Tests Are Good Documentation
A concise code example is better than many paragraphs of documentation. We see this time after time in our consulting work. Far too often, teams produce boilerplate documents that are of little practical value. When programmers need to learn an API, they search for code examples. Tests are among the best code examples because they are concise snippets of code that exercise public APIs.
Tests Reduce the Cost of Change
Tests make it easier to change software because you have confidence that changes do not break existing functionality. When you have good test coverage, you have confidence to explore new design ideas without fear of introducing new bugs.
Poorly-tested software becomes increasingly expensive to change as time goes on. Risk increases dramatically as the system becomes more complex because it becomes more and more likely that changes inadvertently break things that used to work.
Tests Improve Design
Writing tests forces you to make your code testable. Specifically, you tend to rely less on dubious patterns like singletons and global variables, instead making your classes loosely-coupled and easier to use. Code that is tightly-coupled or requires complex initialization is hard to test.
Tests Allow Refactoring
With tests, you are more able to change code throughout the lifetime of an application. Tests provide a safety net, allowing you to refactor at any time without fear of breaking existing code, so you can constantly improve the design of your program.
Tests Constrain Features
Far too often, programmers build fancy frameworks rather than deliver features customers want. When you adopt a test-first approach, you start by writing tests for the current feature. You then implement the feature. When the tests pass, you know you can stop and move to the next feature. Well-tested applications are more easily extended; therefore, you don't have to anticipate what the customer will eventually request.
Tests Defend Against Other Programmers
Textbook code is simple, but real-world problems are hard. We find that in real applications, you often encounter very subtle bugs due to Java bugs, quirky business rules, operating system differences, etc. These bugs may only manifest themselves under very peculiar scenarios.
Let's suppose you find that a payroll calculation routine removes a zero from everyone's salary, but only if the routine runs at 11:59 PM on New Year's Eve. Now, suppose that the bug fix involves a single-line code change.
Without a test, another programmer may come in and change that code. Unless they run the application at 11:59 PM on New Year's Eve, they won't know that they just re-introduced the bug and will cause countless bounced checks next year. With a test, however, you can ensure that when the programmer changes the code, the test breaks and informs the programmer of the problem.
Testing Is Fun
If you thrive on challenges, then testing is a lot of fun. Coming up with automated tests is difficult, requiring creative solutions for complex problems. Just like coding is an art, testing is an art.
In many organizations, testing is relegated to the least-experienced programmers. We often encounter the misconception that testing consists of people completing written checklists as they manually execute the application. This approach is completely unscalable, because it takes longer and longer for humans (monkeys?) to test every feature as the application grows.
Modern OO languages like Java are complex, particularly when it comes to dependencies between classes. One change can easily introduce bugs in seemingly unrelated classes. Gone are the days when each character-based screen is a standalone program. OO apps are far more complex and demand automated tests.
Writing automated tests is harder than writing the code itself, in many cases. The most expert programmers are the best testers. When faced with seemingly mundane coding tasks, coming up with creative tests provides an intellectual challenge that expert programmers thrive on.
Beginners typically need expert assistance when writing tests. This is where pair-programming helps, because experts work side-by-side with beginners as they learn the art of testing.
Testing Forces You to Slow Down and Think
When adding a new feature or refactoring an existing solution, testing forces you to think about what the code is supposed to accomplish. By writing tests first, you think about how to use the public API and what the ultimate outcome should be. Thus you end up with a clean and simple design that does exactly what you expect it to do.
Testing Makes Development Faster
On a class-by-class basis, testing slows you down. It takes time to think about and produce good tests. But as time goes on, your overall velocity increases because you are not constantly worrying about breaking existing code as you add new features.
We have also found that with good tests, we can ignore internal implementation details during the first iteration. Provided that we get the public API right, we can improve internal design and algorithms later, again without fear of breaking something. We have used this specifically for things like sorting algorithms. In the first iteration, we do something quick and dirty, like a bubble sort. We can always come back later and replace it with a better algorithm, if necessary.
Tests Reduce Fear
One of the biggest fears that programmers encounter is making a change to a piece of code and not knowing what is going to break. Having a complete test suite allows programmers to remove the fear of making changes or adding new features. We have found that we do not hesitate to change and improve well-tested code, whereas we fear changing untested code.