Lucas
Lucas

Reputation: 55

How do I stub exception with error in RSpec?

I not sure how to approach the test when the it enters in the exception part for following ".get" method:

/api/reddit_client.rb:

module Api
    class RedditClient

      def self.get(path, access_token)
        begin
          response = RestClient.get(
            path,
            { :Authorization => "Bearer #{access_token}" }
          )

          json_parse(response)
        rescue RestClient::ExceptionWithResponse => e
          logger.error "%%% Something went wrong in request post"
          logger.error "%%% It fails with error: #{e.http_code}"
          logger.error "%%% And with message: #{e.message}"

          { "message" => e.message, "error" => e.http_code }
        end
      end

      ...
      ...
      ...

        private

        def json_parse(response)
          JSON.parse(response.body)
        end
    end
end

I want it to test if it raise the "RestClient::ExceptionWithResponse", for that I did the follow:

/api/reddit_client_spec.rb:

require 'rails_helper'

RSpec.describe Api::RedditClient do
    let(:path) { 'https://www.somerandomapi.com' }
    let(:access_token) { 'f00TOk3N' }

    describe '.get' do
      subject { described_class.get(path, access_token)}

      context 'when not authorized' do
        before do
          allow(described_class)
            .to receive(:get)
            .and_raise(RestClient::ExceptionWithResponse)
        end

        it 'returns hash with error infos' do
          expect{ subject }.to raise_error(RestClient::ExceptionWithResponse)
        end
      end
    end
end

What is tricking me is that I also want to test if the Rails.logger.error was called 3 times too, and check my hash error return. How to test this cases?

Upvotes: 3

Views: 10659

Answers (2)

asimhashmi
asimhashmi

Reputation: 4378

For checking if Rails logger is called 3 times with different messages, you can combine the receive and ordered methods.

Also use RestClient instead of Api::RedditClient in order to execute the method and catch the exception.

Your code will be like this:

 context 'when not authorized' do
   before do
     allow(RestClient) # <- Note that you should use RestClient instead of Api::RedditClient 
       .to receive(:get)
       .and_raise(RestClient::ExceptionWithResponse)
   end

   it 'should raise error' do
     expect(Rails.logger).to receive(:error).with("%%% Something went wrong in request post").ordered
     expect(Rails.logger).to receive(:error).with(""%%% It fails with error: 401"").ordered
     expect(Rails.logger).to receive(:error).with("%%% And with message: Exception Message").ordered
     
     expect{ subject }.to raise_error(RestClient::ExceptionWithResponse)
   end
 end

To check the return response you can rescue the exception inside test and check the return type

it 'returns hash with error infos' do
   expect{ subject rescue nil}.to include({ "message" => "Exception Message", "error" => 401 })
end

Hope it helps

Upvotes: 3

rogercampos
rogercampos

Reputation: 1724

You can't stub Api::RedditClient.get itself because then the code you want to test will not execute at all.

Instead, stub RestClient.get for example:

allow(RestClient)
  .to receive(:get)
  .and_raise(RestClient::ExceptionWithResponse)

About asserting, rspec has ways to check if a given method has been called N times:

https://relishapp.com/rspec/rspec-mocks/v/3-5/docs/setting-constraints/receive-counts

expect(Rails.logger).to receive(:error).exactly(3).times

Upvotes: 2

Related Questions