[rspec-users] How To Spec Controllers with Finders

David Chelimsky dchelimsky at gmail.com
Sat Jan 6 08:20:51 EST 2007


On 1/6/07, s.ross <cwdinfo at gmail.com> wrote:
> What I came up with was:
>
> context "The cart controller" do
>      controller_name "carts"
>      integrate_views
>
>      setup do
>          @cart = mock("cart")
>          @cart.stub!(:new_record).and_return(false)
>          @cart.stub!(:new).and_return(@cart)
>          @cart.stub!(:total_price).and_return(35.00)
>          @cart.stub!(:total_quantity).and_return(1)
>          @cart.stub!(:empty?).and_return(false)
>
>          @line_item = mock("line_item")
>          LineItem.stub!(:find_by_id).and_return(@line_item)
>          @line_item.should_receive(:update_attribute).with(:quantity,
> "1").and_return(true)

I try to avoid using should_receive in setup, so I'd do this here:

@line_item.stub!(:update_attribute).and_return(true)

and then override the method stub w/ a mock expectation in the specify
block. The idea is that you put all of the stuff in setup that must be
there for the code to execute, and put the details in the specs that
are interesting for that spec.

>          @line_item.stub!(:extension).and_return(25.00)
>      end
>
>      specify "should allow change of quantity" do
>          post :change_quantity, {:id => 1, :quantity => 1}
>          response.should_be_success
>          assigns[:extension_id].should == "extension1"
>          assigns[:line_item].should_be(@line_item)
>          controller.should_render_rjs :replace_html, 'cartstatus',
> "(1 item, $35.00)"
>      end
> end
>
> but somehow it seems a bit brittle. This is really what should happen
> if a user changes quantity, but the mocks seem like they won't flex
> or bend easily for further reuse. Any thoughts on this?

Within this context they absolutely will flex or bend by overriding
the method stubs (functioning as defaults) w/ mock expectations.

Also, I might approach this differently. The behaviour here that you
want to spec is that when changing quantity some things should happen.
So....

context "Given a request to change quantity, the CartController should" do
  controller_name "carts"
  integrate_views

  setup do
    @cart = mock("cart")
    @cart.stub!(:new_record).and_return(false)
    @cart.stub!(:total_price).and_return(35.00)
    @cart.stub!(:total_quantity).and_return(1)
    @cart.stub!(:empty?).and_return(false)
    Cart.stub!(:new).and_return(@cart)

    @line_item = mock("line_item")
    @line_item.stub!(:update_attribute).and_return(true)
    @line_item.stub!(:extension).and_return(25.00)
    LineItem.stub!(:find_by_id).and_return(@line_item)

    post :change_quantity, {:id => 1, :quantity => 1}
  end

  specify "respond w/ success" do
    response.should_be_success
  end

  specify "assign exension_id" do
    assigns[:extension_id].should == "extension1"
  end

  specify "assign line_item" do
    assigns[:line_item].should_be(@line_item)
  end

  specify "update the html (using rjs)" do
    controller.should_render_rjs :replace_html,
      'cartstatus',
      "(1 item, $35.00)"
  end
end

This would then read:

Given a request to change quantity, the CartController should
- respond w/ success
- assign exension_id
- assign line_item
- update the html (using rjs)

If you're concerned about reuse of the stubs, you can do this:

module Stubs
  def new_stub_cart
    cart = mock("cart")
    cart.stub!(:new_record).and_return(false)
    cart.stub!(:total_price).and_return(0.0)
    cart.stub!(:total_quantity).and_return(0)
    cart.stub!(:empty?).and_return(false)
    cart
  end

  def new_stub_line_item
    line_item = mock("line_item")
    line_item.stub!(:update_attribute).and_return(true)
    line_item.stub!(:extension).and_return(0)
    line_item
  end

  def stub_cart
    @stub_cart ||= new_stub_cart
  end

  def stub_line_item
    @stub_line_item ||= new_stub_line_item
  end
end

context "Given a request to change quantity, the CartController should" do
  include Stubs
  controller_name "carts"
  integrate_views

  setup do
    Cart.stub!(:new).and_return(stub_cart)
    LineItem.stub!(:find_by_id).and_return(stub_line_item)
  end
  ...
end

Now any new spec that requires specific values can use
stub_cart.should_receive(:total_price).and_return(25.00), for example.


>
> On Jan 5, 2007, at 2:14 PM, Courtenay wrote:
>
> > try this:
> >
> >
> > @line_item = mock("line_item")
> > LineItem.should_receive(:find_by_id).with(3).and_return(@line_item)
> > @line_item.should_receive(:update_attribute).with(:quantity, 1)
> >
> > get :change_quantity, :id => 3, :quantity => 1
> >
> >
> > On 1/5/07, s.ross <cwdinfo at gmail.com> wrote:
> >> Given this code (which renders rjs), I'm faced with the fixture-
> >> driven way of spec-ing or mocking. How the heck to you mock this so
> >> the code at line (2) and (4) work right? I'm still struggling with
> >> mocks but it seems like this can be done. Forgive the naivety of this
> >> question.
> >>
> >> 1.  def change_quantity
> >> 2.    @line_item = LineItem.find_by_id(params[:id])
> >> 3.    unless @line_item.nil?
> >> 4.      @line_item.update_attribute(:quantity, params[:quantity])
> >> 5.      @extension_id = "extension#{params[:id]}"
> >> 6.    end
> >> 7.  end
> >>
> >> Thanks
> >> _______________________________________________
> >> rspec-users mailing list
> >> rspec-users at rubyforge.org
> >> http://rubyforge.org/mailman/listinfo/rspec-users
> >>
> > _______________________________________________
> > rspec-users mailing list
> > rspec-users at rubyforge.org
> > http://rubyforge.org/mailman/listinfo/rspec-users
>
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>


More information about the rspec-users mailing list