Following Corey Haines on twitter is such a good idea. Today he dropped this gem:
Please read, I'm not going to paraphrase it. Nevermind, I will. But it will be really short. Poorly designed code that is tested with a high level of isolation causes a problem. You can't refactor it with confidence because the tests, or the mocks, are blocking your progress. So let's talk about how they impede refactoring first.
Tests prevent behavior from flowing 'up'. For instance, if you mistakenly coupled a model class to a view tier class you could not 'refactor' this problem without breaking the tests for your model. Most likely, the test will fail because the API of the class has changed, you no longer pass the request.
For Mocks it is the opposite. You cannot move behavior 'down'. Let's say I have leaked some model behavior into my controller and I wish to move it to the appropriate model. If my test uses the real model, not a mock, this is not a problem. I can move the behavior and my controller test will still pass. Now I can safely write model tests for the new behavior and delete the controller test that now duplicates it.
If I have mocked the model, however, my controller test will fail when I attempt to move the behavior 'down' into the model. One offense is not an issue. I was involved in one system where an entire test suite was thrown away because of improperly used mocks. Death by a thousand paper cuts.
I have no universal solution for this problem - as it is a problem of my skill as a designer and the skill of my peers. However, I have come up with two helpful guidelines.
Only directly test code you like
I dislike code when I'm unsure of its design. I also dislike code that appears volatile. As my confidence has grown I find myself more comfortable with my early design decisions and more willing to glorify my creations with their own test sooner. When I am not confident I keep my tests at a high enough level where I can refactor freely. As my confidence grows in a system I move towards higher levels of isolation.
Only mock code you like
For all the same reasons as above. Mocks crystalize an API for a class and can cause a lot of problems if the class is used often in a system and the class it is poorly designed. As the ugliness and volatility of code increase, mock it less.
In Conclusion
As with everything, the value of isolation in testing is not an absolute. For younger developers I suggest beginning with a state-based testing approach over using mocks pervasively. A state-based style will afford you maximum flexibility to refactor your design. As your experience grows begin to use mocks to solve some of the problems that state-based testing will invariably bring about.
That is the path I travelled and it worked out alright for me.
Thursday, October 29, 2009
Friday, August 7, 2009
A log4j adapter for Rails
This current client application I'm working on is a mixed bag of Ruby and Java parts. All our existing logging for the older Java parts uses log4j and we wanted to unify our Rails logging with that. I spent a couple of hours putting together this adapter we now use. If you need something similar it should give you a good place to start.
1 | class Log4jAdapter |
Wednesday, August 5, 2009
A Scala version of Unle Bob's lazy PI sequence in Clojure
I follow Uncle Bob on twitter and I'm also currently learning Scala. When I saw his tweet about a lazily evaluated infinite PI sequence in Clojure I couldn't resist giving it a shot in Scala. Here's my result (it works!). Feedback is absolutely welcome.
1 | //Gives us the :: syntax for streams. From http://www.codecommit.com/blog/scala/infinite-lists-for-the-finitely-patient. |
Monday, August 3, 2009
Rails threadsafe! and Engines
If you're planning to use the combination of engines and threadsafe! you'll need to do some extra work. Due to a bug in Rails the engine load paths are not added to the eager_load_paths at startup time. This makes engines unavailable in threadsafe! mode.
Fortunately, working around this problem is simple. Just subclass the plugin loader like this:
And wire up your new loader in your environment.rb:
Fortunately, working around this problem is simple. Just subclass the plugin loader like this:
1 | class EagerLoader < Rails::Plugin::Loader |
And wire up your new loader in your environment.rb:
1 | #require the plugin loader |
Thursday, June 25, 2009
Tuesday, May 5, 2009
Mathematicians should never code alone.
Case in point:
1 | if (xGrad * yGrad <= (float) 0 /*(1)*/ |
Thursday, April 30, 2009
Ruby's object.methods reflection in Scala
Update: Just found out the Scala REPL in trunk has tab completion, which does pretty much exactly this with a lot less fuss. Cool.
If you're coming from the Ruby world to learn Scala you'll learn quickly how much you miss Ruby's elegant reflection. On day 5 of my Scala adventure I took a crack at using Java's reflection APIs to do the job. I think it worked out pretty well. I'd love to hear what you think about it.
get the code here
If you're coming from the Ruby world to learn Scala you'll learn quickly how much you miss Ruby's elegant reflection. On day 5 of my Scala adventure I took a crack at using Java's reflection APIs to do the job. I think it worked out pretty well. I'd love to hear what you think about it.
get the code here
1 | //names of all methods, including super classes |
Subscribe to:
Posts (Atom)
