Over the past couple of years, I have taken quite an interest to software testing. I’ve been reading up on the subject and playing around with different testing tools and techniques, trying to find ways to better test my software. I know from experience that many developers write off testing as boring, pointless, or too difficult. However, I don’t believe that any of these arguments pan out. I’ve also come to better understand the need for comprehensive tests that run on their own and report any problems that they find.
I’ve found that some of the challenges I’ve faced trying to test my team’s production code have been as difficult, or more difficult than solving some of our business issues. Several of these issues have been very fun to solve. Not only did I have to face issues regarding how to test a given piece of code (which were numerous), but also how to go about automating all of our different tests. I am also currently looking into ways to make these automated tests run faster, so we can get notified quickly if our continuous integration build breaks.
As for testing being pointless, well, that argument just doesn’t hold up at all. Sure, comprehensive test coverage, even 100% coverage, will not prevent bugs from creeping their way into your code. But, assuming that you write a new test for each bug you find, it will ensure that once you squash that bug it will never come back. A good test suite acts as a safety net, letting you know immediately if code you are introducing breaks other existing code. These are just the functional benefits. Good tests also serve as excellent documentation on how to use your class or your system. This documentation, unlike some other forms of documentation that is separated from the code, never goes out of date. If it does, it usually results in a test breaking.
Testing can be challenging, but it is far from impossible. In fact, the more you do it, the easier it gets. Testing can be much more difficult on code that was never designed to be tested. Refactoring is the best way to make your code more testable. And, as a side benefit, making your code more testable usually means decoupling it from the rest of the system (always a good thing). However, due to time constraints or other issues, refactoring to this degree may not always be possible. In that case, several tools exist to make testing possible. Mocking tools like EasyMock, jMockit, and JMock allow you to mock out parts of your system that are not decoupled via an interface. Although this is not ideal, it does provide a means for you to test your previously untestable code. If your code is already written to use interfaces over implementations, dependency injection tools like Spring and Guice give you a great means to inject mock objects into your code at runtime.
You’ve probably noticed that I have not mentioned one form of testing over another (unit, integration, performance, etc). That is because everything above applies to all forms of testing. And, in addition to what is listed here, each of these testing types has their own list of benefits. My next several posts will will focus on a particular testing type, and talk about that type in much more detail.