【Rails】APIテストの書き方


概要

Railsで作成されたAPIのテストについてこちらを参考に必要最低限の情報をまとめました。
実際にこちらの記事で作成したAPIに対してテストを書いていきます。

何をテストする?

適切なAPIはHTTPのレスポンスのステータスコードと実際のデータを含んだレスポンスボディを返します。
よって主にその2つをテストしていきます。

ステータスコード

通常APIによって返されるステータスコードは以下の4つに分類されます。
APIの動きによってこちらのコードと照らし合わせる事でテストします。

  • 200: OK - リクエストは成功し、レスポンスとともに要求に応じた情報が返される。
  • 401: Unauthorized - 認証失敗。認証が必要である。
  • 403: Forbidden - 禁止されている。リソースにアクセスすることを拒否された。
  • 404: Not Found - 未検出。リソースが見つからなかった。

レスポンスボディ

GETリクエストの場合は単にリクエストボディに要求したデータが入っているかをテストします。
POSTPUTDELETE リクエストの際にはそれぞれ要求したデータ通りの動きをするかテストします。

APIのテストはインテグレーションテスト

APIのテストは特定のURLに対してデータの要求等をした際の動きをテストするので、インテグレーションテスト(結合テスト)として扱います。

※通常Railsで結合テストを行う際にはcapybaraというgemがよく使われますが、こちらはAPIのテストをする際には適していません。詳しくはこちらから

RspecのRequest Specsを使用する。

APIだからといって特別なテストツール等は使用する必要はなく通常のrailsアプリで頻繁に使用されるrspecを使用してテストします。
APIに対するリクエストに関してテストですのでrspec/requests/ディレクトリ下にテストを作成していきます。

実装

上記で挙げたポイントを以下にまとめましたので、こちらを意識して実際に簡易的なテストを書いていきます。
(※1番上にも挙げましたがこちらのAPIにテストを書いていきます。)
(※あくまでも簡易的なテストですので、とてもシンプルな構造で書いていますが、実際には状況に応じてここからテストを充実させる必要が考えられます。)

ここまでのポイント

  1. ステータスコードとレスポンスボディをテストする。
  2. APIのテストはインテグレーションテスト
  3. rspecを使用しrspec/requests/ディレクトリ下にテストを作成する。

下準備

factory_botを使用してデータを作成しますので以下の手順でセットアップします。

$ gem install factory_bot_rails
Gemfile
group :development, :test do
  gem 'factory_bot_rails'
end
spec/factories/posts.rb
FactoryBot.define do
  factory :post do
    title { 'title' }
  end
end

GET リクエスト

GETリクエストでは正しくデータを取得した際にステータスコード200が返るか、正しく要求したデータが取得できたかをテストします。

/api/v1/postsに対するテスト。

spec/requests/api/v1/posts_spec.rb
require 'rails_helper'

describe 'PostAPI' do
  it '全てのポストを取得する' do
    FactoryBot.create_list(:post, 10)

    get '/api/v1/posts'
    json = JSON.parse(response.body)

    # リクエスト成功を表す200が返ってきたか確認する。
    expect(response.status).to eq(200)

    # 正しい数のデータが返されたか確認する。
    expect(json['data'].length).to eq(10)
  end
end

/api/v1/posts/:idに対するテスト。

spec/requests/api/v1/posts_spec.rb
require 'rails_helper'

describe 'PostAPI' do
  it '特定のpostを取得する' do
    post = create(:post, title: 'test-title')

    get "/api/v1/posts/#{post.id}"
    json = JSON.parse(response.body)

    # リクエスト成功を表す200が返ってきたか確認する。
    expect(response.status).to eq(200)

    # 要求した特定のポストのみ取得した事を確認する
    expect(json['data']['title']).to eq(post.title)
  end
end

POSTリクエスト

POSTリクエストでは正しくデータが作成できたか、正しくデータを作成した際にステータスコード200が返るかをテストします。

require 'rails_helper'

describe 'PostAPI' do
 it '新しいpostを作成する' do
    valid_params = { title: 'title' }

    #データが作成されている事を確認
    expect { post '/api/v1/posts', params: { post: valid_params } }.to change(Post, :count).by(+1)

    # リクエスト成功を表す200が返ってきたか確認する。
    expect(response.status).to eq(200)
  end
end

PUTリクエスト

PUTリクエストでは正しくデータを編集した際にステータスコード200が返るか、正しくしたデータが編集できたかをテストします。

require 'rails_helper'

describe 'PostAPI' do
  it 'postの編集を行う' do
    post = create(:post, title: 'old-title')

    put "/api/v1/posts/#{post.id}", params: { post: {title: 'new-title'}  }
    json = JSON.parse(response.body)

    # リクエスト成功を表す200が返ってきたか確認する。
    expect(response.status).to eq(200)

    #データが更新されている事を確認
    expect(json['data']['title']).to eq('new-title')
 end
end

DELETEリクエスト

DELETEリクエストでは正しくしたデータが削除できたか、正しくデータを削除した際にステータスコード200が返るかをテストします。

require 'rails_helper'

describe 'PostAPI' do
  it 'postを削除する' do
    post = create(:post)

    #データが削除されている事を確認
    expect { delete "/api/v1/posts/#{post.id}" }.to change(Post, :count).by(-1)

    # リクエスト成功を表す200が返ってきたか確認する。
    expect(response.status).to eq(200)
  end
end

参考

Rails API Testing Best Practices