Rspec test public controller method passing params to private controller method

764 views Asked by At

This is my controller:

class PlansController

  def use_new_card
    @user = User.find_by_id(new_card_params[:user_id])
    if new_stripe_card
    ...
  end

  private

  def new_card_params
    params.permit(:user_id, :stripeToken)
  end

  def new_stripe_card
    StripeService.new({user_id: @user.id, customer_id: @user.stripe_customer_id, source: new_card_params[:stripeToken]}).new_card
  end
end

I'm trying to write a controller spec that tests that when the proper parameters are POST to the use_new_card method, then new_stripe_card's StripeService.new gets these parameters appropriately.

My spec looks like this so far:

describe "when proper params are passed" do
  before do
    @user = User.create(...)
  end

  it "should allow StripeService.new to receive proper parameters" do
    StripeService.should_receive(:new).with({user_id: @user.id, customer_id: @user.stripe_customer_id, source: "test"})
    post :use_new_card, user_id: @user.id, stripeToken: "test"
  end
end

But with that I'm getting

Failure/Error: post :use_new_card, user_id: @user.id, stripeToken: "test"
 NoMethodError:
   undefined method `new_card' for nil:NilClass

Totally fair, but I'm not sure how to fix this... I can't just stub new_card because a stubbed method on a nil object will still throw this error (I tried adding a StripeService.any_instance.stub(:new_card).and_return(true) already into the before block)

1

There are 1 answers

2
zetetic On BEST ANSWER

Stubbed methods return nil by default. Use and_return to specify the value returned by the stub::

StripeService.should_receive(:new).and_return(whatever)

or using the newer syntax

expect(StripeService).to receive(:new).and_return(whatever)

EDIT

Pardon my hand-waving. Your stub must return an object that will act like an instance of StripeService to the extent required for the purposes of the test. So for example:

let(:new_card) { double }
let(:new_stripe_service) { double(new_card: new_card) }
...
expect(StripeService).to receive(:new).and_return(new_stripe_service)

Now when the test refers to new_stripe_service, RSpec will return a test double that has a method stub named #new_card, which itself is a double. You can add whatever additional stubs you need to make the test pass. Alternatively, you can return an actual instance of StripeService for new_stripe_service.