What I mean by this, is that sometimes architects look to simplify and improve testability, at the expense of other important forces.
For example, I'm reviewing a very complicated application, made so by extensive use of design patterns that overly favor testing, e.g. IoC, DI, AOP, etc...
Now, typically I like these things, but this system should have been much simpler - though not just a simple web frontend for CRUD on a db, it still not MUCH more complicated than that (even considering some internal workflows, processes, etc). On the other hand, just reviewing the code becomes a major pain in the heinie, barely readable (even though its well written), and coding it must have been a pain.
The implemented complexity is a clear violator of KISS (the principle, NOT the band)... and the "only" benefit is improved testability, using testing frameworks and mocks and and...
Now, before you TDD fans jump me, I'm not belittling the importance of testability, but I'm questioning the supremacy of consideration of this specific force (against all the others).
Or did I miss something?
I'd like to add another point - it does seem to me that all this talk of "testability" is with regards specifically to unit testing, which differs from overall system testing, and can result in missed tests when the individual units are integrated together. At least, that seems the point of the IoC/DI for testing...
Also, I'd point out that this system (and others I've seen preached) only have a single concrete object per interface, and the IoC/DI is only intended for - you guessed it - replacing the concrete objects with testing mockups for testing only.
I felt the need to add this quote from Wikipedia on IoC:
Whereas the danger in procedural programming was to end with spaghetti code, the danger when using Inversion of Control is ending with macaroni code
Yup, that expresses my feeling exactly :D
TDD done well can improve readability. TDD done poorly, that is without consideration of other important principles, can reduce readability.
A guy I worked with in the mid-90s would say "You can always make a system more flexible by adding a layer of indirection. You can always make a system simpler by removing a layer of indirection." Both flexibility and simplicity are important qualities of a system. The two principles can often live together in harmony, but often they work against each other. If you go too far towards one extreme or the other, you move away from the ideal that exists where these two principles are balanced.
TDD is partly about testing, partly about design. TDD done poorly can tend too much towards either flexibility or simplicity. It can push towards too much flexibility. The objects become more testable, and often simpler, but the inherent complexity of the domain problem then is pushed out of the objects into the interaction of the objects. We gained flexibility, and to the naive eye, it can look as though we've gained simplicity because our objects are simpler. The complexity, however, is still there. It's moved out of the objects, and into the object interaction, where it's harder to control. There are code smells that can act as red flags here - a system with hundreds of small objects and no larger objects is one, lots of objects with only one-line methods is another.
TDD done poorly can move in the other direction as well, that is, towards too much simplicity. So, we do TDD by writing the test first, but it has little impact on our design. We still have long methods and huge objects, and those are code smells that can red-flag this problem.
Now TDD will not by its nature knock you off-balance in either direction, provided it's well-applied. Use other practices to keep you on track. For example, draw pictures of what you're doing before you do it. Obviously, not all the time. Some things are far too simple for that. Some pictures are worth saving, some are just sketches that help us to visualize the problem, and we are, by varying degrees, mostly visual learners. If you can't draw a picture of the problem, you don't understand it.
How will this help with TDD? It will help to keep a system from going too far on the flexibility side, away from the simplicity side. If you draw a picture and it's ugly, that's a red flag. Sometimes it's necessary, but often when you draw the picture, your mind will quickly see things that can be simplified. The solution becomes more elegant and simplified, easier to maintain, and more enjoyable to work on. If you can't or won't draw pictures of your system, you're losing this opportunity to make your software more solid, more elegant, more beautiful to see and easier to maintain.
Applying this comes with experience, and some coders will never understand the value that a good balance provides. There's no metric that you can run that tells you you're in the right place. If someone gives you a prescribed method to arrive at that harmonious point, he's lying to you. More importantly, he's probably lying to himself without realizing it.
So, my answer to your question is 'yes': test everything without forgetting the other good principles.
Any good practice will throw you off-course if it's not balanced with other good practices.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With