6
votes

I'm trying to test my API controller with RSPEC. The action in controller have a service that I want to stub/mock. But I got error :

Failure/Error: expect(Api::V1::PaymentsController).to receive(:init_alias).and_return(svc) Api::V1::PaymentsController does not implement: init_alias

Here my test :

  let(:alias_svc) { instance_double(AliasService) }

  describe 'POST /payment/card' do
    it 'returns urls (return, success, fail)' do
      expect(Api::V1::PaymentsController).to receive(:init_alias).and_return(alias_svc)
      expect(alias_svc).to receive(:insert)

      post '/payment/card'
    end
  end

And the controller :

module Api
  module V1
    class PaymentsController < ApiController
      before_action :init_alias, only: [:register_card]

      # POST /payment/card
      def register_card
          @url = @svc.insert current_user
      end

      private
      def init_alias
        @svc = AliasService.new
      end

    end
  end
end

Edit :

I tried to delete the first line (expect(Api::V1::PaymentsController).to receive(:init_alias).and_return(alias_svc)) but I got

Failure/Error: expect(svc).to receive(:insert) (InstanceDouble(AliasService) (anonymous)).insert(*(any args)) expected: 1 time with any arguments received: 0 times with any arguments

1

1 Answers

6
votes

Controller methods are instance methods. Therefore, Api::V1::PaymentsController does not implement init_alias.

RSpec allows to use expect_any_instance_of:

expect_any_instance_of(Api::V1::PaymentsController).to receive(:init_alias).and_return(alias_svc)

Edit

You send your message to @svc. Therefore, you should mock this instance variable to your controller.

You can do something like this:

let(:alias_svc) { instance_double(AliasService) }
controller.instance_variable_set(:@svc, alias_svc)

More details.