0

I need to deal a lot with one external API. Each instance should give access to namespace resources, e.g. client.accounts.payout_history, client.accounts.list. To do so I've created a client using Faraday gem which now I want test by RSpec with build-in Faraday::Adapter (docs for the reference). Here is my code:

Fake client to have control of what's going on

# clients/isor/fake_client.rb
module Isor
  class FakeClient
    BASE_URL = 'https://test-api.net/api/v1/'

    def initialize(stubs:, api_key: 'fake')
      @api_key = api_key
      @stubs = stubs
    end

    def accounts
      Resources::Accounts.new
    end

    def connection
      @connection ||= Faraday.new do |conn|
        conn.url_prefix = BASE_URL
        conn.request :json
        conn.response :json, content_type: 'application/json'
        conn.adapter :test, @stubs
      end
    end

    attr_reader :api_key
  end
end

Place where all http methods are handled:

# clients/isor/resource.rb
module Isor
  class Resource
    def initialize
      @client = client
    end

    def get(url, params: {}, headers: {})
      handle_response client.connection.get(url, params, default_headers.merge(headers))
    end

    # (...) other http methods

    private

    def client
      return @client ||= Client.new unless Rails.env.test?

      @client ||= FakeClient.new(stubs: @stubs)
    end

    def default_headers
      { Authorization: "Bearer #{client.api_key}" }
    end

    def handle_response(response)
      return response.body if response.success?
    # (...) some code if error
    end
  end
end

Class which I want to test:

# clients/isor/resources/accounts.rb
module Isor
  module Resources
    class Accounts < Isor::Resource
      def initialize(stubs: nil)
        @stubs = stubs
      end

      def payout_history(platform_merchant_id:)
        get("merchants/#{platform_merchant_id}/payouts")
      end

      # (...) other endpoints
    end
  end
end

My RSpec test:

require 'rails_helper'

RSpec.describe Isor::Resources::Accounts do
  let(:stub) do
    Faraday::Adapter::Test::Stubs.new do |stub|
      stub.get("api/v1/merchants/#{platform_merchant_id}/payouts") do
        [200, { 'Content-type' => 'application/json' }, 'test response']
      end
    end
  end
  let(:platform_merchant_id) { '1234' }
  subject { described_class.new(stubs: stub) }

  describe '#payout_history' do
    it 'return payout history' do
      expect(subject.payout_history(platform_merchant_id:)).to eq('test response')
    end
  end
end

For reasons unknown to me I get an error:

Faraday::Adapter::Test::Stubs::NotFound: no stubbed request for get https://test-api.net/api/v1/merchants/1234/payouts
mr_muscle
  • 2,536
  • 18
  • 61
  • 1
    You need to stub `"api/v1/merchants/#{platform_merchant_id}/payouts"` instead of `"api/v1/merchants/#{platform_merchant_id}"` – Sampat Badhe Jul 26 '22 at 01:19
  • @SampatBadhe Unfortunately it's just my typo/copying error, it didn't change anything. Question updated btw. – mr_muscle Jul 26 '22 at 02:12
  • Oh ok. You might need to stub entire url not just path. check with `https://test-api.net/api/v1/merchants/#{platform_merchant_id}/payouts` – Sampat Badhe Jul 26 '22 at 02:30

0 Answers0