An interesting thread came up in the rspec-users mailing list about the costs and overheads of using mocks compared with the benefits (as discussed in "Independently testing models, views and controllers").
The basic arguments of the initial poster can be paraphrased as:
- If the API of a class being mocked changes, then specs which mock the class must be updated as well:
- This is duplicative because parallel changes have to be made in multiple sites.
- Because the mock’s behaviour is constant, the underlying API can change and specs which depend on the API won’t fail because the mock will continue to deliver the old behaviour.
- This latter problem is exacerbated when working in teams, where a person changing an API might not know about all the places where it is being mocked.
The counter-solutions offered were:
- Don’t use mocks (or use them less).
- Don’t change APIs (or change them less).
- Rely on integration testing to catch bugs caused by changing APIs when specs that use mocks fail to catch them.
Another, hypothetical solution was:
- Baked in support in RSpec to use mocks by default, but have the runner optionally use the real objects instead when a particular command line switch is passed in.
I had to agree that the original poster’s complaints are all valid to some extent (yes, mocking can involve some overhead) and that his solutions are common sense to a degree (especially the one about keeping your APIs stable; it’s not so much about eschewing change as about using processes which help you design good APIs which won’t need to be changed too often).
The idea of being able to swap real objects back in in place of the mocks is an exciting one.
But for me the real scene-stealer, and the reason why mocks are just too good to pass up despite their costs, is that they enable you to start developing your views before you’ve written your controllers, your controllers before you’ve finished your models and so on. Decoupling is a nice bonus, but the real deal is the way you can use mocks to do incremental Behaviour-Driven Development before the rest of the pieces of the puzzle are in place.
Pat Maddox wrote that he often uses the real objects in controller specs and really only uses mocks for cases where he can’t verify state (such as when he expects a call to be made to some remote system).
One of the points Dave Astels always hammers on about BDD and interaction-based testing is that it’s not testing, it’s a design tool. I’ve found that with mocks I tend to arrive at a better design earlier on than I do without mocks.
One thing about TDD and the notion of a "design tool" - it’s an ongoing process. If you want to end up w/ real models in your specs, you can still use mocks to figure out what they should do, then replace them w/ the real deal later. This is not nearly as expensive as it sounds, and lets you focus on one thing at a time.
I think this latter point is absolutely key; mocks can be tremendously powerful during the early stages of development (during the initial design phase), but there is no reason why you should feel compelled to keep using them latter on the process when they start to feel like too much of a maintenance burden and an impediment to change.
Although there is supposed to be a clean separation between model, view and controller in MVC, in reality the Rails designers grant views and controllers a special relationship status by putting them both together in a gem called
ActionPack. The framework also weakens the separation by making it easy to share methods between controller and helper, and in fact even arranges for controller instance variables to be available in views and helpers as instance variables to those classes as well.
Ironically, despite the tight binding between view and controller, it is precisely at this level that I find it easiest to use mocks. It is very clean and easy to write a view and specs for it before you’ve even started the controller, or even the model.
In the case of
ActiveRecord we have another tight binding: the Ruby abstraction and the database underneath it. I don’t think it’s a bad thing for your model specs to hit the database; that’s what
ActiveRecord is built to do.
The trickiest case for me is the relationship between controllers and models. Keep your controllers simple, they say. Keep your SQL down in the model. Skinny controller, fat model, they say. All of this leads controllers to depend intimately on their models. It’s easy to write models without knowledge of the controller; in this case mocks will be easy. But it’s hard to write controllers without touching the models; you can use mocks to avoid touching them, but there will be cases in which the complexity of setting up the mock is too much to make it justifiable. In those cases it is better to forgo the decoupling benefit and just settle for the ease of using the real model; as a bonus you’re getting some additional impromptu integration testing that you didn’t ask for.
Using mocks in controller specs is still a good idea for the simple cases where things don’t get too impractical, and of course they’re absolutely essential in those cases where you want to start working on your controller before you’ve even written your model code.