概要
APIで共通の処理がある場合に、concernsディレクトリ以下のファイルに共通処理を書きDRYにしたい。
そのときにRSpecをどう書くかというお話です。
APIだけでなく、controllerの場合でも同様の方法が使えます。
方法
API(or controller)のconcernモジュールのテストで困るのは、エンドポイントをどうするかという点です。
方法としては、
- モジュールをincludeしているAPIそれぞれにRSpecを書く
- モジュールをincludeしているAPIのうち代表してひとつにのみRSpecを書く
- 一般的なAPIをテストの時にのみ定義して、その一般的なAPIに対してRSpecを書く。
といったものがあるかと思いますが、今回は最後の方法を紹介します。
大まかに、以下の手順で行います。
- RSpecの中でfakeのAPI、エンドポイントを作成する
- fakeのAPIに当該モジュールをincludeする
- そのエンドポイントに対してリクエストを送りモジュールのテストをする
具体例
以下はinternalなAPIの認証のための処理で、正しいトークンを送っていない場合にBadRequestを発生させるモジュール。
このような処理はinternalなAPI全てで必要になるため、concerns以下に切り出している。
app/api/concerns/application_token_error_handler.rb
module ApplicationTokenErrorHandler
extend ActiveSupport::Concern
included do
before do
if params[:application_token] != Settings.application_token
raise ActionController::BadRequest.new("Invalid application token #{params[:application_token]}")
end
end
end
end
これに対するRSpecは以下のように書けます。
ポイントはRSpecの中で一般的なanonumousというエンドポイントを作成して、そこにリクエストを送るということです。
spec/requests/api/concerns/application_token_error_handler_spec.rb
require 'rails_helper'
describe ApplicationTokenErrorHandler, type: :request do
before do
# 1. fakeのAPIクラスとエンドポイントを定義
class FakeApi < Grape::API
# 2. テストしたいモジュールをincludeする
include ApplicationTokenErrorHandler
resources :anonymous do
params do
requires :application_token, type: String
end
get '/' do
return status 200
end
end
end
# APIのルーティングを行うクラスにmountする(詳細は記事の範囲外なので省略)
class API < Grape::API
mount FakeApi
end
# 3. 上で定義したエンドポイントをリクエストする
get '/anonymous', { application_token: application_token }
end
# テスト終了後に定義した定数を削除する
after { Object.send :remove_const, :FakeApi }
#
# あとはいつも通りのAPIのテスト
#
# 正しいapplication_tokenをパラメタにしてリクエストした場合は200になる
context 'application_token is right' do
let(:application_token) { Settings.application_token }
it { expect(response.status).to eq 200 }
end
# application_tokenが間違っている場合は400になる
context 'application_token is mistaken' do
let(:application_token) { 'invalid_token' }
it { expect(response.status).to eq 400 }
end
end
参考
grapeによるAPIの作成方法。
http://qiita.com/anoworl/items/756f01cc3d188ebad139
Controllerのconcernsモジュールをテストする例。
http://stackoverflow.com/questions/22055889/how-to-test-a-controller-concern-in-rails-4