【Rails】基本的なAPIテストの書き方

# 概要
Railsで作成されたAPIのテストについて[こちら](https://matthewlehner.net/rails-api-testing-guidelines)を参考に必要最低限の情報をまとめました。
実際に[こちらの記事](https://qiita.com/k-penguin-sato/items/adba7a1a1ecc3582a9c9)で作成した[API](https://github.com/K-Sato1995/MyApi)に対してテストを書いていきます。

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

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

- __200__: __OK__ - リクエストは成功し、レスポンスとともに要求に応じた情報が返される。
- __401__: __Unauthorized__ - 認証失敗。認証が必要である。
- __403__: __Forbidden__ - 禁止されている。リソースにアクセスすることを拒否された。
- __404__: __Not Found__ - 未検出。リソースが見つからなかった。

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

# APIのテストはインテグレーションテスト
APIのテストは特定のURLに対してデータの要求等をした際の動きをテストするので、[インテグレーションテスト(結合テスト)](http://e-words.jp/w/%E7%B5%90%E5%90%88%E3%83%86%E3%82%B9%E3%83%88.html)として扱います。

_※通常Railsで結合テストを行う際には`capybara`というgemがよく使われますが、こちらはAPIのテストをする際には適していません。[詳しくはこちらから](https://www.varvet.com/blog/capybara-and-testing-apis/)_。

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

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

__ここまでのポイント__

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

## 下準備
[factory_bot](https://github.com/thoughtbot/factory_bot_rails)を使用してデータを作成しますので以下の手順でセットアップします。

```console
$ gem install factory_bot_rails
```

```ruby:Gemfile
group :development, :test do
  gem 'factory_bot_rails'
end
```

```ruby:spec/factories/posts.rb
FactoryBot.define do
  factory :post do
    title { 'title' }
  end
end
```
## `GET` リクエスト
`GET`リクエストでは正しくデータを取得した際に`ステータスコード200`が返るか、正しく要求したデータが取得できたかをテストします。

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

```ruby: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`に対するテスト。
```ruby: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`が返るかをテストします。

```ruby
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`が返るか、正しくしたデータが編集できたかをテストします。

```ruby
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`が返るかをテストします。

```ruby
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](https://matthewlehner.net/rails-api-testing-guidelines)