module MyApiClient::Stub

Test helper module for RSpec

Constants

ERROR_MESSAGE

Public Instance Methods

stub_api_client(klass, **actions_and_options) click to toggle source

Returns a stubbed arbitrary MyApiClient instance.

@param klass [Class]

Stubbing target class.

@param actions_and_options [Hash]

Stubbing target method and options

@example

api_client = stub_api_client(
  ExampleApiClient,
  get_users: {                                     # Returns an arbitrary pageable response
    pageable: [ { id: 1 }, { id: 2 }]              # for `#pageable_get`.
  },
  get_user: { response: { id: 1 } },               # Returns an arbitrary response.
  post_users: { id: 1 },                           # You can ommit `response` keyword.
  patch_user: ->(params) { { id: params[:id] } },  # Returns calculated result as response.
  put_user: { raise: MyApiClient::ClientError }    # Raises an arbitrary error.
  delete_user: {
    raise: MyApiClient::ClientError,
    response: { errors: [{ code: 10 }] },          # You can stub response and status code
    status_code: 403,                              # with exception.
  }
)
response = api_client.get_user(id: 123)
response.id # => 1

@return [InstanceDouble]

Returns a spy object of the stubbed ApiClient.
# File lib/my_api_client/rspec/stub.rb, line 70
def stub_api_client(klass, **actions_and_options)
  instance = instance_double(klass)
  actions_and_options.each { |action, options| stubbing(instance, action, options) }
  instance
end
stub_api_client_all(klass, **actions_and_options) click to toggle source

Stubs all instance of arbitrary MyApiClient class. And returns a stubbed arbitrary MyApiClient instance.

@param klass [Class]

Stubbing target class.

@param actions_and_options [Hash]

Stubbing target method and options

@example

stub_api_client_all(
  ExampleApiClient,
  get_users: {                                     # Returns an arbitrary pageable response
    pageable: [ { id: 1 }, { id: 2 }]              # for `#pageable_get`.
  },
  get_user: { response: { id: 1 } },               # Returns an arbitrary response.
  post_users: { id: 1 },                           # You can ommit `response` keyword.
  patch_user: ->(params) { { id: params[:id] } },  # Returns calculated result as response.
  put_user: { raise: MyApiClient::ClientError }    # Raises an arbitrary error.
  delete_user: {
    raise: MyApiClient::ClientError,
    response: { errors: [{ code: 10 }] },          # You can stub response and statu code
    status_code: 429,                              # with an arbitrary error.
  }
)
response = ExampleApiClient.new.get_user(id: 123)
response.id # => 1

@return [InstanceDouble]

Returns a spy object of the stubbed ApiClient.
# File lib/my_api_client/rspec/stub.rb, line 38
def stub_api_client_all(klass, **actions_and_options)
  instance = stub_api_client(klass, **actions_and_options)
  allow(klass).to receive(:new).and_return(instance)
  instance
end

Private Instance Methods

agent() click to toggle source
# File lib/my_api_client/rspec/stub.rb, line 160
def agent
  instance_double(Sawyer::Agent).tap do |agent|
    allow(agent).to receive(:parse_links) do |data|
      data ||= {}
      links = data.delete(:_links)
      [data, links]
    end
  end
end
generate_stubbed_response(options, *request) click to toggle source
# File lib/my_api_client/rspec/stub.rb, line 84
def generate_stubbed_response(options, *request) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  case options
  when Proc
    stub_as_resource(options.call(*request))
  when Hash
    if options[:raise] # rubocop:disable Style/GuardClause
      raise process_raise_option(options[:raise], options[:response], options[:status_code])
    elsif options[:response]
      stub_as_resource(options[:response])
    elsif options[:pageable].is_a?(Enumerable)
      stub_as_pageable_resource(options[:pageable].each, *request)
    else
      stub_as_resource(options)
    end
  else
    stub_as_resource(options)
  end
end
process_raise_option(exception, response, status_code) click to toggle source

Provides a shorthand for `raise` option. `MyApiClient::Error` requires `MyApiClient::Params::Params` instance on initialize, but it makes trubolesome. `MyApiClient::NetworkError` is more. If given a error instance, it will return raw value without processing.

@param exception [Clsas, MyApiClient::Error] Processing target. @param response [Hash] describe_response_here @param status_code [Integer] describe_status_code_here @return [MyApiClient::Error] Processed exception. @raise [RuntimeError] Unsupported error class was set.

# File lib/my_api_client/rspec/stub.rb, line 123
def process_raise_option(exception, response, status_code)
  case exception
  when Class
    params = MyApiClient::Params::Params.new(nil, stub_as_response(response, status_code))
    if exception == MyApiClient::NetworkError
      exception.new(params, Net::OpenTimeout.new)
    else
      exception.new(params)
    end
  when MyApiClient::Error
    raise ERROR_MESSAGE if response.present?

    exception
  else
    raise "Unsupported error class was set: #{exception.inspect}"
  end
end
stub_as_pageable_resource(pager, *request) click to toggle source
# File lib/my_api_client/rspec/stub.rb, line 103
def stub_as_pageable_resource(pager, *request)
  Enumerator.new do |y|
    loop do
      y << generate_stubbed_response(pager.next, *request)
    rescue StopIteration
      break
    end
  end.lazy
end
stub_as_resource(params) click to toggle source
# File lib/my_api_client/rspec/stub.rb, line 151
def stub_as_resource(params)
  case params
  when Hash  then Sawyer::Resource.new(agent, params)
  when Array then params.map { |hash| stub_as_resource(hash) }
  when nil   then nil
  else params
  end
end
stub_as_response(params, status_code) click to toggle source
# File lib/my_api_client/rspec/stub.rb, line 141
def stub_as_response(params, status_code)
  instance_double(
    Sawyer::Response,
    status: status_code.presence || 400,
    headers: {},
    data: stub_as_resource(params),
    timing: 0.123
  )
end
stubbing(instance, action, options) click to toggle source
# File lib/my_api_client/rspec/stub.rb, line 78
def stubbing(instance, action, options)
  allow(instance).to receive(action) do |*request|
    generate_stubbed_response(options, *request)
  end
end