【Rails/Rspec】リクエストスペックでAPIをテストする際に、よくやること
リクエストスペックの特徴
- HTTP 動詞に対応するメソッド(get 、post 、delete 、 patch)を使う。
- capybaraは使わない。
- HTTPのレスポンスのステータスコードと実際のデータを含んだレスポンスボディを返す
200: OK - リクエストは成功し、レスポンスとともに要求に応じた情報が返される。 401: Unauthorized - 認証失敗。認証が必要である。 403: Forbidden - 禁止されている。リソースにアクセスすることを拒否された。 404: Not Found - 未検出。リソースが見つからなかった。
リクエストスペックでよく使うヤツ
JSON.parse
与えられた JSON 形式の文字列を Ruby オブジェクトに変換して返す。
→JSON形式の文字列からRubyのHashへ変換することで、WebAPIで取得したJSONデータをRubyで使える様にするリクエストヘッダーをセットしておく。
→ヘッダ情報がないとテストが通らないことがある。
get api_v1_articles_path, headers: { 'CONTENT_TYPE': 'application/json', 'ACCEPT': 'application/json' }
(spec/requests/api/v1/articles_spec.rb) require 'rails_helper' RSpec.describe "Api::V1::Articles", type: :request do describe "GET /api/v1/articles" do it "掲示板一覧を返す" do create_list(:article, 10, user: user) get api_v1_articles_path, headers: { 'CONTENT_TYPE': 'application/json', 'ACCEPT': 'application/json' } // JSON.parse(body)でもいけたけど、response.bodyにしておく json = JSON.parse(response.body) expect(response).to have_http_status(200) expect(json["data"].count).to eq(10) end end end
letを使って、headers情報を格納しておく。
let(:headers) do { 'Content-Type' => 'application/json', 'Accept' => 'application/json' } end
letで数字を変数(numとか)に入れておくのもアリ
RSpec.describe 'Api::V1::Articles', type: :request do describe 'GET /articles' do let(:article_num) { 10 } before do user = create(:user) create_list(:article, article_num, user: user) end it 'returns fincle articles in json format' do get api_v1_articles_path, headers: { CONTENT_TYPE: 'application/json', ACCEPT: 'application/json', } expect(JSON.parse(body)['data'].count).to eq(article_num) expect(response).to be_successful expect(response).to have_http_status(:ok) end end end
上記テストの、JSON.parse(response.body)["data"]の結果
JSON.parse(response.body)["data"] => [{"id"=>"1", "type"=>"article", "attributes"=>{"title"=>"title-1", "contents"=>"contents-1", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"1", "type"=>"user"}}}}, {"id"=>"2", "type"=>"article", "attributes"=>{"title"=>"title-2", "contents"=>"contents-2", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"2", "type"=>"user"}}}}, {"id"=>"3", "type"=>"article", "attributes"=>{"title"=>"title-3", "contents"=>"contents-3", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"3", "type"=>"user"}}}}, {"id"=>"4", "type"=>"article", "attributes"=>{"title"=>"title-4", "contents"=>"contents-4", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"4", "type"=>"user"}}}}, {"id"=>"5", "type"=>"article", "attributes"=>{"title"=>"title-5", "contents"=>"contents-5", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"5", "type"=>"user"}}}}, {"id"=>"6", "type"=>"article", "attributes"=>{"title"=>"title-6", "contents"=>"contents-6", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"6", "type"=>"user"}}}}, {"id"=>"7", "type"=>"article", "attributes"=>{"title"=>"title-7", "contents"=>"contents-7", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"7", "type"=>"user"}}}}, {"id"=>"8", "type"=>"article", "attributes"=>{"title"=>"title-8", "contents"=>"contents-8", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"8", "type"=>"user"}}}}, {"id"=>"9", "type"=>"article", "attributes"=>{"title"=>"title-9", "contents"=>"contents-9", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"9", "type"=>"user"}}}}, {"id"=>"10", "type"=>"article", "attributes"=>{"title"=>"title-10", "contents"=>"contents-10", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"10", "type"=>"user"}}}}]
記事詳細のレスポンスに
JSON.parse
[7] JSON.parse(body) => {"data"=>{"id"=>"1", "type"=>"article", "attributes"=>{"title"=>"title-11", "contents"=>"contents-11", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"2", "type"=>"user"}}}}} [8] JSON.parse(body)["data"] => {"id"=>"1", "type"=>"article", "attributes"=>{"title"=>"title-11", "contents"=>"contents-11", "status"=>"draft"}, "relationships"=>{"user"=>{"data"=>{"id"=>"2", "type"=>"user"}}}} [9] JSON.parse(body)["data"]["id"] => "1" [10] JSON.parse(body)["data"]["attributes"] => {"title"=>"title-11", "contents"=>"contents-11", "status"=>"draft"} [11] JSON.parse(body)["data"]["attributes"]["title"] => "title-11" レスポンスボディ?の数 [12] JSON.parse(body).count => 1 relationship [14] JSON.parse(body)["data"]["relationships"] => {"user"=>{"data"=>{"id"=>"2", "type"=>"user"}}} ステータスコード [3] response.status => 200
attributes_for
POSTメソッドで、 - ファクトリからテスト用の属性値をハッシュとして作成する。複数のパラメータを送信する場合に、個々にパラメータ名を付けるのではなく、キーと値を組み合わせたハッシュを作成してくれる。 - buildならモデルオブジェクトを作成する。
https://qiita.com/_kotaro/items/adbc355bfb550b8b2150
FactoryBotのデータ
(spec/factories/users.rb) FactoryBot.define do factory :user do sequence(:name) { |n| "user-#{n}" } sequence(:email) { |n| "user-#{n}@example.com" } password { 'password' } end end
- attributes_for
let!(:user_attributes) { attributes_for(:user) } => {:name=>"user-1", :email=>"user-1@example.com", :password=>"password"}
- build
let!(:user_attributes) { build(:user) } => #<User:0x00007fcf5a68cff0 id: nil, name: "user-1", email: "user-1@example.com", crypted_password: nil, salt: nil, created_at: nil, updated_at: nil>
- create
let!(:user_attributes) { create(:user) } => #<User:0x00007fb222ebcf18 id: 1, name: "user-1", email: "user-1@example.com", crypted_password: "$2a$04$rslb/9FwCJVJFYmR.LeLR.8JjYOt2kEYCh8d2F5E1JJGRJ1lZ/BiS", salt: "hoCH88wcyx3PFVqxduem", created_at: Sun, 26 Jul 2020 02:15:36 UTC +00:00, updated_at: Sun, 26 Jul 2020 02:15:36 UTC +00:00>
to_json
RubyのHash形式のデータを、JSON形式の文字列に変換してくれる。
[1] user_attributes => {:name=>"user-1", :email=>"user-1@example.com", :password=>"password"} [2] user_attributes.to_json => "{\"name\":\"user-1\",\"email\":\"user-1@example.com\",\"password\":\"password\"}"