[rspec-users] Mocks? Really?

Zach Dennis zach.dennis at gmail.com
Wed Dec 26 09:19:29 EST 2007


On Dec 26, 2007 1:26 AM, Jay Donnell <jaydonnell at yahoo.com> wrote:
> > If the
> > implementation of a model is
> > wrong it isn't the job of the controller spec to
> > report the failure.
> > It's the job of the model spec or an integration
> > test (if its an
> > integration related issue) to report the failure.
>
> It seems that it would be very easy to change a model,
> thereby breaking the controller, and not realize it.
> Let's say that we decide to change the implementation
> of a model, how do you then go about finding the
> controllers that need to be updated?

The integration test will die if you broke functionality.

> I know this is
> the classic argument between classicists and mockists,
> but I don't see the benefit of this type of strict
> mocking. If the integration test is required then what
> benefit are we getting from the mock and is it worth
> the cost?
>

At either level an integration is required. I prefer extracting it out
into its own test so I can simplify (by which I mean isolate) my
controller. The other option is to give the test of the controller the
dual responsibility of testing the controller works correctly by
itself and also that it works correctly with real models.

By isolating the controller and doing interaction-based testing I find
that I end up with simpler controllers and more simple objects. I
think this is because my tests become increasingly painful to write
with the more crap I try to shove on my controller.  I have learned to
listen to them and start extracting out other objects when my tests
become painful because it's usually a sign.

I also prefer acceptance test driven development, which is TDD on top
down development so interaction-based testing is important since the
model is usually one of the last things I create.


>
> >
> > Also, controllers can achieve a better level of
> > programming toward the
> > "interface" rather then a concrete class by using
> > dependency
> > injection.
>
> I don't see any reason to use DI in a dynamic language
> like ruby. I also see no reason in this specific case.
> Let's assume we're working on a rails social
> networking site. If we have a Blog controller and a
> Blog model class there is no reason to use DI to
> inject the blog model in the blog controller. It isn't
> removing unneeded coupling, it's adding unneeded
> complexity. In java this injection is necessary to
> make things like testing easier, but it is wholly
> unnecessary in a language like ruby.

This is the Jamis Buck argument. DI is unneeded in Ruby as it is
implemented in Java. Needle and Copland are Java implementations in
Ruby and they should be avoided. I do not agree that DI is wholly
unneeded. In my experience the Injection library has been very
lightweight and it has worked well in my controllers for Rails apps.
The only way to get around DI is to have every class/module know about
every other class/module it deals with OR to reopen classes and
override methods which would supply an object. Both of these have
their shortcomings.

I am not advocating using DI for the sake of DI, but it can be useful.
For example, I often extract out date, authentication, etc. helpers
and managers. So in my BlogsController there may be a reference to the
Blog model because as you say it is not unneeded coupling, however my
BlogsController requires authentication and rather then dealing with a
LoginManager directly it deals with a @login_manager. Having my
BlogsController know about the LoginManager implementation is unneeded
coupling. It needs to be able to authenticate, it doesn't need to know
which implementation it uses to authenticate.

>From a development perspective you end up with a declarative list of
objects your implementation will rely on. It's highly readable what
your controller depends to do its job. This is a supporting +1 in my
opinion.

>
>
> > If you don't
> > isolate then you end
> > up with a lot of little integration tests. Now when
> > one implementation
> > is wrong you get 100 test failures rather then 1 or
> > 2, which can be a
> > headache when you're trying to find out why
> > something failed.
>
> This has never been a headache for me. If you run your
> tests often you'll know what was changed recently and
> it's trivial to find the problem. Also, if you run
> localized tests frequently you'll see the error
> without seeing the failures that it causes through out
> the test suite and you still get the benefit of mini
> integration tests ;)

I agree we should be running tests frequently.

One of the things you didn't hit up is how you test objects which
coordinate interactions vs those that do the work? Those branch/leaf
object scenarios. How do you see testing those? Do see the separation
of testing concerns as non-existent because only doing state-based
testing will cause every failure (even when an object is working
correctly, but the objects its coordinating are broken)?

I guess if the LoginManager is working correctly it seems wrong in
principle and practice to have it be red if the User object is working
is broken.

-- 
Zach Dennis
http://www.continuousthinking.com


More information about the rspec-users mailing list