[rspec-devel] Fwd: [ rspec-Bugs-6905 ] Mocking rails associations where the association is validated

aslak hellesoy aslak.hellesoy at gmail.com
Mon Nov 27 16:05:03 EST 2006


On 11/27/06, Pat Maddox <pergesu at gmail.com> wrote:
> On 11/27/06, David Chelimsky <dchelimsky at gmail.com> wrote:
> > On 11/27/06, Pat Maddox <pergesu at gmail.com> wrote:
> > > Another question :)
> > >
> > > When I first wrote the add_person method, my spec looked like this:
> > >
> > >   specify "should accept a new person" do
> > >     @user.people.size.should_be 0
> > >     @user.add_person Person.new
> > >     @user.people.size.should_be 1
> > >   end
> > >
> > > That works, of course, and lets me infer that I have the new user.
> > > However I think it's ugly that I'm accessing people to verify
> > > something about the user...
> > >
> > > So then I decided to change it to this:
> > >
> > >   specify "should accept a new person" do
> > >     mock_person = mock("person")
> > >     mock_people = mock("people")
> > >     @user.should_receive(:people).and_return(mock_people)
> > >     mock_people.should_receive(:<<).with(mock_person)
> > >     @user.add_person mock_person
> > >   end
> > >
> > > That feels like the more RSpecy way to do this, despite being longer.
> > > In both cases they dig into people (either checking the size or
> > > checking that it calls <<), but in the latter case it verifies
> > > behavior, rather than inferring it from state.  Does that sound right?
> >
> > This one is tricky. There are multiple perspectives on how much
> > mocking you should do in model specs. One view is that model specs are
> > really small integration specs (model class + database). Another view
> > is that you should do what you did above, mocking out the other
> > players where possible.
> >
> > I'm leaning towards the first view - that model specs are like little
> > integration specs. My thinking is that because the way AR is designed,
> > the things you end up mocking are often internal to AR, which is a
> > huge mocking no-no (mock YOUR code, not other people's).
>
> Weren't we mocking other people's code in the controller specs as well?
> ARClass.should_receive(:find).with(1).and_return(some_mock_object)
> ARClass.should_receive(:new).with(:name => "name").and_return(other_mock_object)
>
> I'm not really sure what the difference is between mocking that out
> and mocking players.  At first I thought that it's because the find
> and new calls appear in my code, but so does players <<.
>
>
> > In this case you could use this to make it feel more RSpecy without
> > mocking anything:
> >
> > specify "should accept a new person" do
> >   lambda do
> >     @user.add_person
> >   end.should_change(@user.people.size).by(1)
> > end
> >
> > I'm not a huge fan of going after @user.people.size here, but I think
> > that @user.number_of_people might be overkill.
>
> After thinking about this some more, I think I like the other version
> with lots of mocks.  To me, it boils down to what I think is a
> revelation I had last night...using the specs do define behavior,
> rather than infer it from state.
>
> The one problem I see with my example was what if the User object
> doesn't define a players method?  Then the spec passes fine, but the
> code blows up of course.  This would be easily solved if you ensure
> that partial mocks can only mock methods that the object already has.
>
> for example, I've got a user object that has players in this case...when I do
> user.should_receive(:players)
> then I think the should_receive call out to call
> user.respond_to?(players) before mocking the method.  That seems
> reasonable to me for partial mocks - we're just substituting in some
> code for behavior that should already exist, because the existing code
> is too complex or expensive and doesn't need to be executed at the
> moment.  At the very least, perhaps it could generate a warning such
> as "WARNING - 'people' not defined on user object."  WDYT?
>

That would be possible to bake into RSpec, but it would violate one of
the main principles of BDD. BDD is (among other things) a technique to
*discover* the interfaces of neighbouring objects N while speccing a
class C. And while you're speccing C, N might not even exist.

The WARNING idea is interesting though.

> Pat
> _______________________________________________
> rspec-devel mailing list
> rspec-devel at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-devel
>


More information about the rspec-devel mailing list