He'll tell you that, before he can test module F, he needs to configure modules A, B, C, D and E - and it's completely impossible to test module G because it requires a connection to the Internet or the customer database needs to be available or it will only work with this piece of hardware.
You'll propose all kinds of strategies for how he can overcome these obstacles and he'll shoot each one down one by one. But ... you know what? He'll be right. His code is too hard to test.
So what do we recommend to our unfortunate developer? Should he give up on automated unit testing because his code is too hard to test? Why doesn't everyone have this problem? How do other developers make their code more testable? The answer is simple. They test it. The best way to write testable code is to test it. That last statement is worth repeating.
When I write a test, I want the module I am testing to be self-contained and easy to configure. I want the module to have one, consistent purpose in life. I want its connections to other modules to be explicit and well-defined. I don't want the module to have secret connections to other modules using static or global variables. I don't want it to have annoying little side-effects. When my test is done, I want the module to go away and leave no trace. I don't want one test to interfere with the results of another and I don't want to do a whole bunch of clean up after every test.
Many of the wishes in that little rant probably sound familiar to you. They should. They are the principles of good software design. For years now, the experts have been telling us how to design software....
... but for years we have been ignoring them. Sure, they made a good case for design but many of their reasons were long-term and theoretical. Why waste time defining an interface and passing it as a parameter when we can just create a global variable (or a singleton, if we are pretending to write object-oriented code) and be done with it? To encourage re-use (like that's gonna happen)? To reduce our cyclomatic complexity? Yeah, right.
Finally, we have a practical reason to use good design. Good design makes it easier to test our software. If your module is hard to test because it has too many dependencies - remove the dependencies. If test #87 fails because test #23 changed the value of a global variable - get rid of the global variable. If your module requires an expensive, external resource - refactor until it doesn't. Hide the resource behind an interface and fake out the interface for the purposes of your test.
If you want your code to be easier to test .... test it !
Posted by Kevin Lawrence at January 16, 2004 03:22 PM
TrackBack URL for this entry:
Well said! That is just an absolutely beautiful summary.
I'm going to snag some of the points from this, put them onto a sheet of paper (in 24 point bold!) and tape it over my workstation until it all becomes second nature.
Posted by: DAR on November 18, 2005 07:46 AM