[rspec-devel] understanding Mocks Stubs and ActiveRecord

David Chelimsky dchelimsky at gmail.com
Thu Mar 20 08:07:59 EDT 2008

On Wed, Mar 19, 2008 at 9:42 PM, Bryan Wheelock
<bryan.wheelock at gmail.com> wrote:
> I'm still having a difficult time grasping the use of Stubs and Mocks.
> Does every method you pass to the mock have to stubbed out?
> Do you have to stub out responses to actions on Object attributes?
> If so, then it would seem you could test something that worked as a mock but
> would
> break with the actual database because an attribute was misspelled.
> I'm guessing that what is actually happening is that I can't access the
> model attributes.
> Which would make sense if the Mock and Stub were the only elements of the
> model that existed.
> I also have a question about Activerecord::Base#increment!
> Since it operates on an attribute of Candidate, should the receiver of
> should_receive be Candidate or should it be Candidate.votes
> Regardless, here is the code that has me confused.
> describe "handling GET /affiliates/1" do
>    before(:each) do
>      @candidate = mock_model(Candidate, :id => 1,
>                                         :name => "Mr. Falsechoice"
>                                         :votes => 1 )
>      Candidate.stub!(:find).with(1).and_return(@candidate)
>      Candidate.should_receive(:find).with("1").and_return(@candidate)
>      Candidate.stub!(:increment!).with(:votes).and_return(true)
>      Candidate.should_receive(:increment!).with(:votes).and_return(true)
>    end

Why are you using both stub! and should_receive for the same messages here?

>    def do_get
>      get :show, :id => "1"
>    end
>    it "a request for an Candidate should increment votes by one" do

There's a reason we chose "it" here. Clearly "it a request ..." does
not make any sense. I'd structure this something like:

describe Candidate do
  describe "responding to a request" do
    it " should increment votes" do

>      do_get
>      @candidate.votes.should eql(2)
>      # I don't understand how I'm supposed to mock increment!
>      # things I've tried:
>      # Candidate.should_receive(:increment!)
>      # Candidate.should_receive("increment!")
>      # Candidate.clicks.should_receive(:increment!)
>    end
> end
> # this is the Controller I'm testing
> class CandidatesController < ApplicationController
>     def show
>      @affiliate = Candidate.find(params[:id]).increment!("votes")

This line is the same as this:

@affiliate = Candidate.find(params[:id])

If you think of it that way, there are two things you need to mock here:

affiliate = mock_model(Candidate)

The thing is that since you've done it all in one line, you have to
make sure to return the affiliate from the call to :increment, so ....

affiliate = mock_model(Candidate)

Personally, when I see a need to mock two levels deep like that, I
take that as encouragement to change the design a bit. What I'd like
to see is something like:

affiliate = mock_model(Candidate)

Now the example is cleaner, the resulting controller code is cleaner,
and the work is pushed down to the model, where it belongs IMO.


>      respond_to do |format|
>         format.html # show.html.erb
>         format.xml  { render :xml => @affiliate }
>     end
>   end

> My specs give me this error:
> Mock 'Candidate_1007' received unexpected message :increment! with ("votes")
> I really appreciate any pointers.
> thanks,
> Bryan
> _______________________________________________
>  rspec-devel mailing list
>  rspec-devel at rubyforge.org
>  http://rubyforge.org/mailman/listinfo/rspec-devel

More information about the rspec-devel mailing list