A few years ago I gave a talk that gets into the "why" of the relationship between testability and good design: http://vimeo.com/15007792
It has a rambling start because I was timezone shifted, but gist of it is that whenever we encounter something that makes testing difficult there's some sort of design problem that is causing it. Some are obvious and some aren't.
- Hard to test a class in isolation -> too many dependencies
- Tests fail intermittently -> mutable global variables
- Unable to find a good point to test -> direct access of system externals, i.e., no layering
- All tests can't run because of resource leaks -> code doesn't clean up after itself
- Urge to test something private in a class -> usually an SRP violation
- ...
The list goes on.
The real question is why? Why is testability a proxy for good design?
I think that the reason it is is because the essence of good design is creating things that are understandable - understandable enough to see plainly what they do and understandable to enough to modify and extend.
Automated testing is a cognitive process - it is writing code that understands code. Every step of test is bit of that cognition. If it's hard to write code that "understands" your code, then it's probably hard to understand your code.
I think of it the other way: if it's easy to write code that "understands" your code, then it's probably easy to understand your code, because you can encode (ha!) your understanding precisely enough for a computer to execute it and accurately enough to pass tests.
I don't quite buy the contrapositive, though: I easily understand small functions that touch well-named global variables, as long as it touches maybe 1 or 2 of them, but we wouldn't call such a function particularly "testable". At some point, poor testability and difficulty in understanding correlate, but we can easily exhibit entire classes (ha again!) of easy-to-understand-but-difficult-to-test code.
If we had a catalog mapping test/testing problems to design smells, perhaps we'd find it easier to argue our position more convincingly. A bunch of us keep meaning to write that catalog, but I haven't seen it yet. :)
If it's hard to write code that "understands" your code, then it's probably hard to understand your code.
That seems true. But most TDD people do not claim that, they claim that it it's hard to write fast code that "understands" your code without needing to "understand" anything else on the system, then it's probably hard to understand your code.
Comments
Great article.
A few years ago I gave a talk that gets into the "why" of the relationship between testability and good design: http://vimeo.com/15007792
It has a rambling start because I was timezone shifted, but gist of it is that whenever we encounter something that makes testing difficult there's some sort of design problem that is causing it. Some are obvious and some aren't.
- Hard to test a class in isolation -> too many dependencies
- Tests fail intermittently -> mutable global variables
- Unable to find a good point to test -> direct access of system externals, i.e., no layering
- All tests can't run because of resource leaks -> code doesn't clean up after itself
- Urge to test something private in a class -> usually an SRP violation
- ...
The list goes on.
The real question is why? Why is testability a proxy for good design?
I think that the reason it is is because the essence of good design is creating things that are understandable - understandable enough to see plainly what they do and understandable to enough to modify and extend.
Automated testing is a cognitive process - it is writing code that understands code. Every step of test is bit of that cognition. If it's hard to write code that "understands" your code, then it's probably hard to understand your code.
I think of it the other way: if it's easy to write code that "understands" your code, then it's probably easy to understand your code, because you can encode (ha!) your understanding precisely enough for a computer to execute it and accurately enough to pass tests.
I don't quite buy the contrapositive, though: I easily understand small functions that touch well-named global variables, as long as it touches maybe 1 or 2 of them, but we wouldn't call such a function particularly "testable". At some point, poor testability and difficulty in understanding correlate, but we can easily exhibit entire classes (ha again!) of easy-to-understand-but-difficult-to-test code.
If we had a catalog mapping test/testing problems to design smells, perhaps we'd find it easier to argue our position more convincingly. A bunch of us keep meaning to write that catalog, but I haven't seen it yet. :)
That seems true. But most TDD people do not claim that, they claim that it it's hard to write fast code that "understands" your code without needing to "understand" anything else on the system, then it's probably hard to understand your code.
I can't agree with the fully qualified proposal.