Testability and Good Design

by chromatic

Michael Feathers addresses the question "How do you test private methods?" in The Deep Synergy Between Testability and Good Design. (If you're stuck in a language with a fetish for compile-time access control on the part of the library writer, you have my sympathy.)

The answer is more interesting than the question. As Michael points out, the desire to test private methods extensively often indicates that he has too much behavior in the class. Other comments suggest that the barrier to creating new classes is too high--we don't do it often enough.

I've often suggested that a major benefit of TDD is that it encourages better design, not only because of YAGNI but because you immediately have to use the API you're designing. If that's painful, or awkward, you may need to refactor. Testability is one part of the aesthetic necessary to write good code.


3 Comments

Daniel
2007-09-26 06:56:37
"I’ve often suggested that a major benefit of TDD is that it encourages better design, not only because of YAGNI but because you immediately have to use the API you’re designing."


Actually if the tests drive your development then you are thinking about how you will use the API before and while you are creating it. Brad Cox talked about this 20 years ago before we used words like "unit tests" in this context. He recommended figuring out how your code would exercise a particular API before you designed and wrote it.

chromatic
2007-09-26 11:26:13
@Daniel, exactly! I've seen a lot of people say that you should document your APIs before you write them, but that seems backwards to me. Executable specifications are so much more useful.
Jim Keenan
2007-09-27 08:53:42
While the public/protected/private categorization of methods is not as relevant in Perl as it is in Java, many of Feathers' remarks rang true for me. On the one hand, where I have control over the design of the interface and the implementation -- as in my CPAN modules -- I've increasingly found that not only does "[a]iming for testability actually changes your design," it actually improves it. And it certainly improves the implementation: You quickly learn that untestable code is YAGNI code.


On the other hand, I also work on open source projects where I'm a relative newcomer and where the interface and implementation were decided before I joined the project. My assignment is to write tests for already existing interfaces and implementations. In these contexts I often encounter code that displays the symptom described by Feathers: classes that encapsulate so much that they are not effectively testable through their public interface.


I fully agree with Feathers: "When a bit of functionality is not easily understood by a test at the public interface, the chances of it being easily understood by a human are ridiculously low."