[rspec-users] Preferred way to predict behavior in controller spec?

Patrick J. Collins patrick at collinatorstudios.com
Fri May 4 23:14:50 UTC 2012


So, I have an action like this:

  def destroy
    if purchase.user == current_user && purchase.refund
      redirect_to purchase
    else
      flash[:error] = t("purchases.refund.failure")
      render :show
    end
  end

...  I wanted a controller test to verify the following:

  1.  It's the correct user
  2.  The refund was successful
  3.  User is redirected

...

Initially, I thought this would go something like this:

  describe "#destroy" do
    let(:purchase) { create_purchase }

    it "refunds the buyer" do
      subject.stubs(:current_user).returns purchase.user
      purchase.stubs(:refund).returns true
      put :destroy, { :id => purchase.id }
      response.should be_redirect
    end
  end

WRONG!!!!!!!!!

Obviously, that is not cool because purchase.stubs(:refund) will not be the
right object in the context of the controller......  So since my controller is actually doing the following:

  def destroy
    if purchase.user # etc...
  end

  private

  def purchase
    @purchase ||= Purchase.find(params[:id])
  end

I could do:

subject.stubs(:purchase).returns(purchase)

..  However, now that kind of defeats the purpose of put :destroy, { :id => purchase.id }...

So I didn't really like that-- I am no longer verifying it's the right record,
so maybe the things I wanted to test were actually:

  1.  It finds the purchase
  2.  It's the correct user
  3.  The refund was successful
  4.  User is redirected

........

So I ended up making my test do:

  describe "#destroy" do
    let(:purchase) { create_purchase }

    def do_destroy
      put :destroy, { :id => purchase.id }
    end

    it "refunds the buyer" do
      subject.stubs(:current_user).returns purchase.user
      Purchase.any_instance.stubs(:refund).returns true
      do_destroy
      response.should be_redirect
    end
      subject.stubs(:current_user).returns purchase.user
      Purchase.any_instance.stubs(:refund).returns true

      do_destroy
      response.should redirect_to purchase.offer
    end

    it "does not refund the buyer when it fails" do
      subject.stubs(:current_user).returns purchase.user
      Purchase.any_instance.stubs(:refund).returns false

      put :destroy, :id => purchase.id
      response.should_not be_redirect
    end

    it "does not refund the buyer when it's the wrong user" do
      subject.stubs(:current_user).returns create_user
      Purchase.any_instance.expects(:refund).never

      do_destroy
      response.should_not be_redirect
    end
  end

But then I heard the voice of an old friend in my head, saying (with a long
trailing echo) "any_instance is terrible practice.. never use it!"

So I am curious if anyone has suggestions on how this might be improved?

Thank you.

Patrick J. Collins
http://collinatorstudios.com


More information about the rspec-users mailing list