Hi all - I've been keeping an eye on this thread and I've just been
too busy with holiday travel and book writing to participate as I
would like.

I'm just going to lay out some thoughts in one fell swoop rather than
going back through and finding all the quotes. Hope that works for
you.

First - "mock roles, not objects" - that comes from a paper of the
same name written by Steve Freeman, Nat Pryce, Tim Mackinnon, Joe
Walnes who I believe were all working for ThoughtWorks, London in
2004. They describe using mocks as part of a process to stay focused
on one object at a time and let mock objects help you to discover the
interfaces of the current object's collaborators. My read is that they
do not make a distinction between domain objects and service objects,
though they do make a distinction between "your" objects (which you
should mock) and "everyone else's" (which you should not).

My own approach is largely derived from this document, and I'd
recommend that everyone participating in this thread give it a read:
http://www.jmock.org/oopsla2004.pdf.

I think one place that we tend to get stuck on, and this is true of
TDD in general, not just mocks, is that mocks need not be a permanent
part of any example. Before I encountered Rails it was common for me
to use mocks in a test and then replace them with the mocked object
later. This decision would depend on many factors, and I can't say
that I sought to eliminate mocks when I could, but there were times
when it just made more sense to use a real object once it came to be.

Rails is a different beast because we don't really have a sense of 3
layers with lots of little objects in each. Instead we have what
amount to 3 giant objects with lots of behavior in each and even
shared state across layers. For me, this rationalizes isolating things
with mocks and stubs (which is counter to the recommendation in the
oopsla paper referenced above). Because the framework itself provides
virtually no isolation, the spec suite must if you want isolation.

Zach's idea of branch nodes and leaf nodes really speaks to me. I
don't remember where I read this, but I long ago learned that an ideal
OO operation consists of a chain of messages over any number of
objects, culminating at a boundary object (what Zach is calling a leaf
node). It should also favor commands over queries (Tell Don't Ask), so
while all of the getters we get for free on our AR model objects is
convenient, from an OO perspective it's a giant encapsulation-sieve
(again, more reason to isolate things w/ stubs/mocks in tests).

You might find 
http://www.holub.com/publications/notes_and_slides/Everything.You.Know.is.Wrong.pdf
in regards to this. In this paper, Holub suggests that getters are
evil and we should use importers and exporters instead of exposing
getters/setters. If we were to re-engineer rails to satisfy this,
instead of this in a controller:

def index
  @model = Model.find(params[:id])
  render :template => "some_template"
end

you might see something more like this:

def index
  Model.export(params[:id]).to(some_template)
end

Here Model would still do a query, but it becomes internal to the
Model (class) object. Then it passes some_template to the model and
says "export yourself", at which point the model starts calling
methods like name=self.name. The fact that the recipient of the export
is a view is unknown to the model, so there is no conceptual binding
between model and view.

Ah, just think of how easy THAT would be to mock, and when things are
easy to mock, it means that it is easy to swap out components in the
chain of events, thus using run-time conditions to alter the path of a
given operation through different objects.

There is much, much more to say, but this is all I have time to
contribute right now.

Cheers, and Happy New Year to all!

David
_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to