社内でよくRailsでAPIを書くことが多く、そのテストをするときによく使うAPI用のshared_context
を紹介します。
ApiHepers
以下がshared_context
本体となります。
spec/support/api_helpers.rb
として作成します。
spec/support/api_helpers.rb
module ApiHelpers
shared_context 'api helper', type: :api do
subject { call_api }
let(:path) { nil }
let(:method) { nil }
let(:body) { nil }
let(:header) { nil }
let(:default_header) do
{
'Content-Type' => 'application/json'
}
end
let(:call_api) do
before_call_api if respond_to?(:before_call_api)
body_json = body ? body.to_json : nil
merged_header = default_header.merge(header.to_h)
send(method.to_s.downcase, path, body_json, merged_header)
after_call_api if respond_to?(:after_call_api)
end
end
end
そして、rails_helper
に以下を記載し、作成したmoduleを読み込ませるます。
spec/rails_helpers.rb
# supportが読み込みがコメントアウトされてないか確認
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
RSpec.configure do |config|
# 追加するmodule
config.include ApiHelpers
end
解説
APIの呼び出しの根幹部分はこちらの、call_api
になっています。
処理の仕組みは簡単で、method
に指定した文字列を小文字化 => シンボルに変えて、requestのspecのメソッドとして呼び出しています。
let(:call_api) do
before_call_api if respond_to?(:before_call_api)
body_json = body ? body.to_json : nil
send(method.to_s.downcase.to_sym, path, body_json, default_header.merge(header.to_h))
after_call_api if respond_to?(:after_call_api)
end
そしてこのcall_api
は、基本subject
を呼ぶことで呼ばれるようになっています。
subject { call_api }
また、PUTやPATCHのようにAPIを読んだ後に特定のオブジェクトを更新したい場合のために、after_call_api
というメソッドをcall_api
の後で呼び出すようにしています。
使用例
RSpec.describe API::V1::Sessions do
describe 'POST /api/v1/sign_in', type: :api do
let(:path) { '/api/v1/sign_in' }
let(:method) { 'POST' }
let(:body) do
{
email: 'hoge@example.com',
password: 'password'
}
end
let(:after_call_api) { user.reload }
let(:user) do
user = FactoryGirl.create(:user, email: 'hoge@example.com')
user.set_password_with_encrypt('password')
user.save
user
end
it do
subject
expect(response.status).to eq(201)
json = JSON.parse(response.body)
expect(json[:id]).to eq(user.id)
expect(json[:access_token]).to eq(user.session.access_token)
end
it { expect { subject }.to change(Session, :count).by(1) }
end
重要な部分としては、
describe 'POST /api/v1/sign_in', type: :api do
の部分。
ここのtype: :api
を指定することでAPIのshared_context
を呼び出しています。