Writing Testable Code
An MVB explains what he means by the term 'testable code' and what writing it can help you achieve.
Join the DZone community and get the full member experience.Join For Free
This has become my favorite subject because it turns out that testability, above nearly every other metric, is important to strive for in developing software. And all other code qualities seem to be a reflection of testability.
You may also like: 5 Easy Ways to Write Hard-to-Test Code
When I say testable code, what I mean is code that can be verified programmatically and in a very granular way. When a test fails, we want it to tell us exactly why so that we know what went wrong and can correct the problem immediately. This means that we're verifying very small units of behavior. This not only helps us debug code when there are problems, but it also helps keep us focused on building software that fulfills some desired behavior.
When we write our tests around behaviors rather than the way we implement those behaviors, we are free to change the implementations later, and if the behavior doesn't change, then our tests shouldn't need to change either.
I know people who insist that every method must have a unit test. Well, I'm going to say a word here that I rarely say. That's just WRONG. I don't use that word often because there's usually exceptions or contraindications that make something that's wrong in one situation right in another. But this is not the case here. If you write too many tests or implementation tests when doing TDD, you'll have issues when trying to refactor your code later.
Forget about code coverage for a second and think about testing behaviors. We don't need to test private methods, for example, if they can be exercised through the public methods that call them.
I find that a key characteristic of testable code: It is code that is focused on doing one thing that produces some kind of externally visible result. Unit tests should be simple and easily verifiable.
If the only way you can test the system is by programmatically acting like a user, then you have essentially untestable code. System tests can be useful, but they shouldn't be your only strategy for testing. The days of printing call stacks in running the debugger are far less prominent, thankfully because unit testing frameworks are far more valuable and durable. Testing is an investment, and we want our investments to be valuable in the future.
Writing testable code means that the smallest components are independently verifiable. In order to do this, each component must have its dependencies injected into it. This means that code can't reference global variables or use read/write singletons or service locators, etc. This may be a slightly different way of thinking about building a program than you're used to, but it can be a highly efficient and effective way of building software and it can be programmatically verified.
What are some benefits of testable code?
Well, for one thing, it means that we can automate the verification process so we can keep our software in an always ready to release state. It also makes last-minute changes, the kind of changes that are often very important on a project, trivial and essentially cost-free. These are the same kind of changes that are often exorbitantly expensive to make late in a development project that doesn't use automated regression testing.
Testable code is code of high quality. It's cohesive and loosely coupled. It's well encapsulated and in charge of its own state. In short, it's singularly defined in its own proper place so that it's straightforward to maintain and extend. Testability is one of my great teachers in revealing to me better options when I'm building software.
Note: This blog post is based on a section in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software.
Published at DZone with permission of David Bernstein, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.