Skip to content

Instantly share code, notes, and snippets.

@lucashour
Created July 15, 2020 14:47
Show Gist options
  • Save lucashour/c8442ca35b1e8b422390afb7851035c0 to your computer and use it in GitHub Desktop.
Save lucashour/c8442ca35b1e8b422390afb7851035c0 to your computer and use it in GitHub Desktop.
Integrando una API en Rails
gem 'faraday'
gem 'faraday_middleware'
group :test do
gem 'rspec-rails'
gem 'webmock'
end
# app/services/external/holidays_fetcher.rb
class External::HolidaysFetcher
URL = 'http://nolaborables.com.ar'.freeze
def initialize
@connection = Faraday.new(url: URL) do |faraday|
faraday.request :url_encoded
faraday.response :json, content_type: 'application/json'
faraday.adapter Faraday.default_adapter
end
end
def call(year: Date.current.year, with_optional: true)
parameters = with_optional ? {incluir: :opcional} : {}
response = @connection.get("/api/v2/feriados/#{year}", parameters)
return false unless response.success?
build_holidays(response)
end
private
def build_holidays(response)
response.body.map do |holiday|
External::Holiday.new(holiday)
end
end
end
# spec/services/external/holidays_fetcher_spec.rb
require 'rails_helper'
require 'mocks/holidays_mock'
describe External::HolidaysFetcher do
subject(:fetcher) { described_class.new }
describe '#call' do
shared_examples 'External::Holiday builder' do
it 'returns External::Holiday records' do
holidays = subject
expect(holidays).not_to be_empty
holidays.each do |holiday|
expect(holiday).to be_an_instance_of(External::Holiday)
end
end
end
shared_examples 'Optional External::Holiday builder' do
it 'builds optional External::Holiday' do
expect(subject.any?(&:optional?)).to be true
end
end
shared_examples 'HTTP connection to external API' do
it 'makes an HTTP request to the external API' do
subject
expect(api_mock).to have_been_requested
end
end
shared_examples 'Current year holidays fetcher' do
it 'uses current year as part of the URI' do
subject
uri_regex = /nolaborables\.com\.ar\/api\/v2\/feriados\/#{Date.current.year}/
expect(WebMock.stub_request(:get, uri_regex))
.to have_been_requested
end
end
context "when 'with_optional' argument is not received" do
let!(:api_mock) { HolidaysMock.mock(:all) }
subject { fetcher.call }
it_behaves_like 'External::Holiday builder'
it_behaves_like 'Optional External::Holiday builder'
it_behaves_like 'HTTP connection to external API'
it_behaves_like 'Current year holidays fetcher'
end
context "when 'year' argument is received" do
let(:year) { Date.current.year - 1 }
let!(:api_mock) do
HolidaysMock.mock(
:all,
/nolaborables\.com\.ar\/api\/v2\/feriados\/#{year}/
)
end
subject { fetcher.call(year: year) }
it_behaves_like 'External::Holiday builder'
it_behaves_like 'Optional External::Holiday builder'
it_behaves_like 'HTTP connection to external API'
end
context 'when requesting holidays with optional ones' do
let!(:api_mock) { HolidaysMock.mock(:all) }
subject { fetcher.call(with_optional: true) }
it_behaves_like 'External::Holiday builder'
it_behaves_like 'Optional External::Holiday builder'
it_behaves_like 'HTTP connection to external API'
it_behaves_like 'Current year holidays fetcher'
end
context 'when requesting holidays without optional ones' do
let!(:api_mock) { HolidaysMock.mock(:all_except_optional) }
subject { fetcher.call(with_optional: false) }
it_behaves_like 'External::Holiday builder'
it_behaves_like 'HTTP connection to external API'
it_behaves_like 'Current year holidays fetcher'
it 'does not build any optional External::Holiday' do
expect(subject.any?(&:optional?)).to be false
end
end
context 'when there is a connection error' do
let!(:api_mock) { HolidaysMock.mock(:connection_error) }
subject { fetcher.call }
it { is_expected.to be false }
end
end
end
# spec/mocks/holidays_mock.rb
require 'webmock'
class HolidaysMock
include WebMock::API
URI = /nolaborables\.com\.ar\/api\/v2\/feriados\/\d{4}/.freeze
URI_WITHOUT_OPTIONAL = /nolaborables\.com\.ar\/api\/v2\/feriados\/\d{4}$/.freeze
URI_WITH_OPTIONAL = /nolaborables\.com\.ar\/api\/v2\/feriados\/\d{4}\?incluir=opcional$/.freeze
def self.mock(method, *args)
mock_object.send(method, *args)
end
def all(uri = nil)
stub_holidays(
uri || URI_WITH_OPTIONAL,
File.read('spec/mocks/data/holidays_with_optional.json')
)
end
def all_except_optional
stub_holidays(
URI_WITHOUT_OPTIONAL,
File.read('spec/mocks/data/holidays_without_optional.json')
)
end
def connection_error
stub_request(:get, URI).to_return(
body: '{"error": "Error de conexión"}',
status: 500,
headers: {'Content-Type' => 'application/json'}
)
end
class << self
private
def mock_object
@mock_object ||= HolidaysMock.new
end
end
private
def stub_holidays(uri, response)
stub_request(:get, uri)
.to_return(
body: response,
status: 200,
headers: {'Content-Type' => 'application/json'}
)
end
end
# spec/spec_helper.rb
require 'webmock/rspec'
RSpec.configure do |config|
# ...
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment