背景
最近、個人でRuby on Railsを使ってWebサービスのAPIを開発しています。
その際にRSpecを使って主にRequest specを書きました。
spec形式の書き方で試行錯誤したので公開してみます。
俺流Request spec
まずは、コードを共有させていただきます。
組織のCRADを実装したOrganization APIのrequest specです。
rails_helperやメソッドに処理をさせているところもあって、
わかりづらいかもしれませんが動いているコードの一部をほぼそのまま持ってきました。
RSpec.describe 'Organizations', type: :request do
# 共通のデータ
let!(:user) { create(:user) }
let!(:uneditable_user) { create(:user) }
let!(:organization) { create(:organization) }
let(:prepare_data) { -> {} }
let(:headers) { {} }
let(:params) { {} }
before do
prepare_data.call
end
describe 'GET /index' do
before { get organizations_path, headers:, params: }
context '認証時' do
let(:headers) { user.create_new_auth_token }
context 'クエリパラメータが指定されていない場合' do
it 'リクエストが成功すること' do
expect(response).to have_http_status(:ok)
end
it 'マイボックスが含まれないこと' do
expect(parsed_response.organizations).not_to include(serialize_organization(user.mybox))
end
it 'アクセスできるグループが含まれていること' do
expect(parsed_response.organizations).to include(serialize_organization(organization))
end
it 'アクセスできないグループが含まれないこと' do
expect(parsed_response.organizations).not_to include(serialize_organization(inaccessible_organization))
end
end
describe 'クエリパラメータを指定' do
context 'include_myboxパラメータがtrueの場合' do
let(:params) { { include_mybox: true } }
it 'リクエストに成功すること' do
expect(response).to have_http_status(:ok)
end
it 'マイボックスが含まれること' do
expect(parsed_response.organizations).to include(serialize_organization(user.mybox))
end
end
end
end
context '未認証時' do
it '401エラーになること' do
expect(response).to have_http_status(:unauthorized)
end
end
end
describe 'GET /show' do
let(:request_organization) {}
before { get organization_path(request_organization.name), headers:, params: }
context '認証時' do
let(:headers) { user.create_new_auth_token }
context 'アクセス権限があるグループの場合' do
let(:request_organization) { organization }
it 'リクエストが成功すること' do
expect(response).to have_http_status(:ok)
end
it 'リクエストしたグループが返却されること' do
expect(parsed_response.name).to eq(request_organization.name)
end
end
context 'アクセス権限がないグループの場合' do
let(:request_organization) { inaccessible_organization }
it '404エラーになること' do
expect(response).to have_http_status(:not_found)
end
end
end
context '未認証時' do
let(:request_organization) { organization }
it '401エラーになること' do
expect(response).to have_http_status(:unauthorized)
end
end
end
目指したもの
describeやcontextで定義した内容をその直下に実装することを目指しました。
普通に書くと、どうしてもAPIのリクエスト(上記のコードだと get organizations_path, headers:, params:
)がit付近で呼び出す感じになってしまう。
こんな感じ
describe 'GET /index' do
context '認証時' do
context 'クエリパラメータが指定されていない場合' do
before { get organizations_path, headers: user.create_new_auth_token }
it 'リクエストが成功すること' do
expect(response).to have_http_status(:ok)
end
これががどうもRSpecらしくないような気がしてしまって
let
を駆使して記述してみました。
describe 'GET /index' do
before { get organizations_path, headers:, params: }
context '認証時' do
let(:headers) { user.create_new_auth_token }
context 'クエリパラメータが指定されていない場合' do
it 'リクエストが成功すること' do
expect(response).to have_http_status(:ok)
end
どちらがお好みですか?
実際に書いてみて
今のところ書きやすいし読みやすい。そして、問題も起きていません。
ただ、あまり同じ書き方をしている例が見つけられなかったので、少し不安ではあります。
俺流はだいたい後々後悔するので...(個人開発では、わかっててもやってしまう)
なので、まだオススメはしません。お見せするだけ。