[rspec-users] Mocks? Really?

Zach Dennis zach.dennis at gmail.com
Thu Dec 27 09:13:55 EST 2007


On Dec 26, 2007 3:23 PM, Pat Maddox <pergesu at gmail.com> wrote:
> On Dec 23, 2007 2:27 PM, Zach Dennis <zach.dennis at gmail.com> wrote:
> > I know the questioned are directed towards Dan so I hope you don't me
> > chiming in. My comments are inline.
>
> Thanks a lot for your comments, I really appreciate them.  I've been
> dying to respond to this for the past several days, but haven't had
> internet access.
>
>
> > On Dec 15, 2007 2:17 AM, Pat Maddox <pergesu at gmail.com> wrote:
> > >
> > > On Dec 8, 2007 4:06 AM, Dan North <tastapod at gmail.com> wrote:
> > > > I prefer the mantra "mock roles, not objects", in other words, mock things
> > > > that have behaviour (services, components, resources, whatever your
> > > > preferred term is) rather than stubbing out domain objects themselves. If
> > > > you have to mock domain objects it's usually a smell that your domain
> > > > implementation is too tightly coupled to some infrastructure.
> > >
> > > Assuming you could easily write Rails specs using the real domain
> > > objects, but not hit the database, would you "never" mock domain
> > > objects (where "never" means you deviate only in extraordinary
> > > circumstances)?  I'm mostly curious in the interaction between
> > > controller and model...if you use real models, then changes to the
> > > model code could very well lead to failing controller specs, even
> > > though the controller's logic is still correct.
> >
> > In Java you don't mock domain objects because you want to program
> > toward an interface rather then a single concrete implementation.
> > Conceptually this still applies in Ruby, but because of differences
> > between the languages it isn't a 1 to 1 mapping in practice.
>
> I must be misunderstanding you here, because you say you "don't mock
> domain objects," and the rest of your email suggests that you mock
> basically everything.
>
> <snip>
>
> > > Do you try to test each
> > > class in complete isolation, mocking its collaborators?
> >
> > Yes. The pattern I find I follow in testing is that objects whose job
> > it is to coordinate or manage other objects (like controllers,
> > presenters, managers, etc) are always tested in isolation.
> > Interaction-based testing is the key here. These objects can be
> > considered branch objects. They connect to other branches or to leaf
> > node objects.
> >
> > Leaf node objects are the end of the line and they do the actual work.
> > Here is where I use state based testing. I consider ActiveRecord
> > models leaf nodes.
>
> What about interactions between ActiveRecord objects.  If a User
> has_many Subscriptions, do you mock out those interactions?

For me it depends. If I am testing my User object and it has a custom
method called find_subscriptions_which_have_not_expired_but_which
_has_not_been_read_in_over_n_days. I will not mock out any
interactions with the subscriptions at that point. This is for two
reasons. One, when I first get this to work I may do it in pure ruby
code (no SQL help) just to get it working. At some later date/time
this is going to move to SQL. I want my test to not have to change in
order to do this. If I was interaction-based testing this custom find
method then it wouldn't really help me ensure I didn't break
something. Secondly, I view my model has a leaf node object. Most of
everything my model does I want to state based test the thing to
ensure the results are what I want (and not the interactions).

Some times I find there is a method where I will mock an association
because I truly don't care about the result, and I really only care
about the interaction. For example if User delegates something to the
Subscription class either via a delegate declaration or a simple
method which delegates. For example:

delegate :zip_code, :to => :address

OR

def zip_code
   address.zip_code
end


>  Would you
> still mock them out if User and Subscription were PROs (plain Ruby
> objects) and persistence were handled separately?

Possibly. I think it depends on how the objects used each other, what
kind of mini-frameworks or modules were in place to give
functionality, etc. Since models get most of their functionality
through inheritance of ActiveRecord::Base it would be difficult to
compare w/o knowing how my PROs were hooked up. Composition or
inheritance makes a difference in my head right now. Do you have any
specific concrete examples in mind?

>
>
> > > When you use
> > > interaction-based tests, do you always leave those in place, or do you
> > > substitute real objects as you implement them (and if so, do your
> > > changes move to a more state-based style)?
> >
> > Leave the mocked out collaborators in place. An interaction based test
> > verifies that the correct interaction takes place. As soon as you
> > remove the mock and substitute it with a real object your test has
> > become compromised. It's no longer verifying the correct interaction
> > occurs, it now only makes sure your test doesn't die with a real
> > object.
>
> This leads to perhaps a more subtle case of my previous
> question...ActiveRecord relies pretty heavily on the real classes of
> objects.  To me, this means that it would make more sense to use mocks
> if you didn't use AR, but use the real objects when you are using AR.
> Again, this is only between model objects.  I agree that controller
> specs should mock them all out.

I agree with this. This is largely how I work now as described above.

>
>
> > > I realize that's a ton of questions...I'd be very grateful (and
> > > impressed!) if you took the time to answer any of them.  Also I'd love
> > > to hear input from other people as well.
> > >
> >
> > It's too bad we can't just stand at a whiteboard and talk this out.
> > The answers to these questions could fill a book and email hardly does
> > it justice to provide clear, coherent and complete answers. Not that
> > my response are "answers" to your questions, but it's how I think
> > about testing and TDD.
>
> Thanks again for your thoughtful reply.  Looking forward to hearing a
> little bit more.
>

ditto,


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


More information about the rspec-users mailing list