[Rails] 自分流のrspec(API向け)テンプレート

「APIのテスト書きたいけど、どんな感じでかけばいいのか忘れちゃった！」というときに、とりあえずこのテンプレをコピペしてからコーディングを始めることで超絶時短を狙う。

# テンプレート

**前提**
FactoryBot利用し、Factoryのクラスメソッド実行時に``Factory.``を省略できるような[設定をrails_helperでしている](https://qiita.com/WisteriaWave/items/4ec897ca45047cea3abe)

```spec/requests/【対象リソース名(スネークケース、複数形)】.rb
require 'rails_helper'

RSpec.describe "/<対象リソース名(スネークケース、複数形)>", type: :request do

  # xxx_xxxテーブルのレコード設定
  ## <レコードの説明>
  let!(:xxx_xxx1) { create(:<xxx_xxx>, :<trait>,...) }
  ## <レコードの説明>
  let!(:xxx_xxx2) { create(:<xxx_xxx>, :<trait>,...) }
  ...

  # yyy_yyyテーブルのレコード設定
  ## <レコードの説明>
  let!(:yyy_yyy1) { create(:<yyy_yyy>, :<trait>,...) }
  ## <レコードの説明>
  let!(:yyy_yyy2) { create(:<yyy_yyy>, :<trait>,...) }
  ...

  
  # APIリクエスト関連
  ## 認証トークン
  let!(:token) do
    <認証処理>
  end

  ## リクエストヘッダ(認証OK)
  let!(:valid_headers) { { 'Content-Type' => 'application/json', 'Authorization' => "Token #{token}" } }
  ## リクエストヘッダ(認証NG)
  let!(:valid_headers) { { 'Content-Type' => 'application/json', 'Authorization' => "Token #{token}a" } }

  ## ステータスコード
  let(:res_status) { response.status }
  ## レスポンスボディ(のJSON文字列をパースして、キーがシンボルであるハッシュとして受け取ったもの)
  let(:res_body) { JSON.parse(response.body, { symbolize_names: true }) }
  ## 認証エラー時のレスポンスボディ
  let!(:res_body_unauthorized) { { error: 'unauthorized' } }

　　　　describe '<HTTPメソッド> /<アクション>' do
    context '正常系' do
      # リクエストパラメータ
      let!(:params) { { a: 1, b: 2,... }.to_json }
      # リクエストヘッダ
      let!(:headers) { valid_headers }


      # リクエスト(GET /index時)
      subject { get <リソース名(スネークケース,複数形)>_url, headers: headers }

      # リクエスト(GET /show時)
      # subject { get <リソース名(スネークケース,単数形)>_url(<表示対象のリソースのモデルインスタンス(ActiveRecord)>), params: params, headers: headers }

      # リクエスト(POST /create時)
      # subject { post <リソース名(スネークケース,複数形)>_url, params: params, headers: headers }

      # リクエスト(PUT /update時)
      # subject { put <リソース名(スネークケース,単数形)>_url(<更新対象のリソースのモデルインスタンス(ActiveRecord)>), params: params, headers: headers }

      # リクエスト(DELETE /delete時)
      # subject { delete <リソース名(スネークケース,単数形)>_url(<削除対象のリソースのモデルインスタンス(ActiveRecord)>), headers: headers }

      let(:expected_body) do
        {
          <期待するレスポンスボディの内容>
        }
      end

      it 'ステータスコード: <ステータスコード>, ボディ: <レスポンスボディの概要>, DB: <DBデータの変化概要>' do
        # リクエスト実行
        expect(subject).to change(<リソースのクラス名>, :size).by(<変化したレコード数>)
        # ステータスコードチェック
        expect(res_status).to eq(<ステータスコード>)
        # レスポンスボディチェック
        expect(res_body).to eq(expected_body)
      end
    end

    context '異常系: 認証エラー' do
      # リクエストパラメータ
      let!(:params) { { a: 1, b: 2,... }.to_json }
      # リクエストヘッダ
      let!(:headers) { invalid_headers }
      # リクエスト(GET /index時)
      subject { get <リソース名(スネークケース,複数形)>_url, headers: headers }

      it 'ステータスコード: <ステータスコード>, ボディ: <レスポンスボディの概要>, DB: レコードが作成されない' do
        # リクエスト実行
        expect(subject).to change(<リソースのクラス名>, :size).by(0)
        # ステータスコードチェック
        expect(res_status).to eq(401)
        # レスポンスボディチェック
        expect(res_body).to eq(res_body_unauthorized)
      end
    end

    context '異常系: リクエストパラメータ不備' do
    end
    ...

  end
end
```

# 補足
コードの構造にルールを設け、シンプルに保つことを心がける。

## ①階層構造(describe, context, it)
1. **「チェック対象はdescribe, 条件はcontext, 期待結果はit」**と階層化する。

2. DBのデータやリクエスト、レスポンスに関する定義など、**準備はdescribe, contextで**済ませておく

3. **itでは、``リクエストを投げる → それによる結果をチェックする``に注力**する

## ②準備系の設定(let!, let, before)
後の階層で変数的に使いまわしたい設定は、**let!での定義を基本とし、時間差が必要な場合のみletを使う**
※letは、遅延評価をしっかり意識しないと期待しない挙動をしてしまい、泥沼にハマる可能性が高いため
※後から参照しなくてよい設定は、beforeで定義する慣習らしい。・・・が、コード書いてる内に後から「やっぱり必要でした！」というケースもあるし、**let!でなくbeforeを使うメリットはさほどない**と判断

## ③リクエストのスローとレスポンスのチェック(subject, response)
1. **subjectに、実際にリクエストを投げる処理を記述**する
2. subject内で記述する**APIのパスは、``<bundle exec rails routeで表示されるパスの省略系>_url``**でベタ書きせず書く
3. 2.について、**show, update, delete系のAPIは、その引数に操作対象リソースのモデルインスタンスを記述(``xxx_url(<インスタンス>)``)してやるとidをベタ書きせずに書ける**
4. API実行結果であるレスポンスボディ、ステータスコードはresponseから参照する(response.status, response.body)
※``subjectを使用してリクエストをスローすると、responseでその結果を受け取れる、というrspecの機能``があるのでこれを利用
5. レスポンスボディ(response.body)は、JSON文字列でありチェック時に扱いづらいため、キーはシンボルでハッシュ化する処理を定義しておく
6. 期待するレスポンスボディの定義も、キーがシンボルなハッシュで記述して、3.と突き合わせる形でチェックする
7. [ステータスコード](https://qiita.com/WisteriaWave/items/fc10d58101c425dab19b)のチェックは、数値でベタ書きする(be_successとか書くと、200でも201でもチェック通過してしまい、曖昧性が排除できない)
8. レスポンスのチェックは、「ステータスコード」「レスポンスボディ」「DBのレコード数変化」の3点とする。
※レコードの内容がなにかしら変化する系のアクション(create, update, delete)は、「DBの中身的にもホントに期待する結果になっているのか？」まで見たほうが良い気もするが、個数だけチェックしている記事が多いっぽいのでそうする(腑に落ちてはいない)。

## メモ
ホントはheaderの定義もキーがシンボルのハッシュで書きたい。
しかし、なぜか``Content-type:``がうまくかけない。
なので、渋々キーが文字列のハッシュにした。