RSpec入門　5.コントローラスペック

[前回](https://qiita.com/zeisho/items/deb3b357b22ac8f5c3fc)の続き

RSpecでのコントローラのテストはモデルのテストと統合テストで代替できてしまうため、使う機会は多くありません。
最近では、より高度な統合テストであるシステムスペックが導入されたため、コントローラスペックの出番はさらに減少しました。
とはいえ、既存のプログラムの保守で触れることもあるかと思うので、軽く触れる程度にまとめます。

#静的なコントローラのスペック
レスポンスのステータスが成功しているかをテストします。
まずは、スペックファイルの生成。

```
$ rails g rspec:controller home
```

spec/controllers/home_controller_spec.rbが生成されます。

コントローラがリクエストに対して正常なレスポンスを返すかテストします。

```spec/controllers/home_controller_spec.rb
require 'rails_helper'

RSpec.describe HomeController, type: :controller do
  de
  describe "#index" do
    it "responds successfully" do
      get :index
      expect(response).to be_success
    end
  end
end

```

responseはブラウザに返すべきアプリケーションの全データを保持しているオブジェクトです。
be_successはレスポンスステータスが成功（200）か、それ以外かをチェックするマッチャです。

特定のHTTPレスポンスコードを確認したいときはhave_http_statusが使えます。

```spec/controllers/home_controller_spec.rb
require 'rails_helper'

RSpec.describe HomeController, type: :controller do
  de
  describe "#index" do
    it "responds a 200 response" do
      get :index
      expect(response).to have_http_status "200"
    end
  end
end
```

#認証が必要なコントローラスペック
projectsコントローラのスペックを書いていきます。
このコントローラは、ログイン中のユーザーのみ自分に紐づいたプロジェクトを操作できるという、よくあるCRUDの構造になっています。

```
$ rails g rspec:controller project
```

##テスト前の準備

先ほどと同じく、まずは成功のレスポンスが返るかテストしますが、認証が必要なコントローラのアクションは事前の準備が必要です。

準備は大きく分けて2つ、
1.Deviseのヘルパーモジュールの読み込み
2.ログイン用のヘルパーメソッドの定義
です。順を追って説明します。

###Deviseのヘルパーモジュールの読み込み
Deviseは認証が必要なコントローラのアクションに対して、ユーザーのログイン状態をシミュレートするヘルパーを提供し、テスト実行時はこのヘルパーを使用しています。ですが、RSpecはデフォルトの状態だとこのヘルパーを読み込まないので、使えるように設定を追加します。

```spec/rails_helper.rb
  #この1行を追加します
  config.include Devise::Test::ControllerHelpers, type: :controller
```
この設定で、コントローラスペックでDeviseのヘルパーモジュールが使えるようになります。

###ログイン用のヘルパーメソッドの定義
ユーザーがログインしていない状態でリクエストを送ると、ログインページにリダイレクト（302）が返るので、テストのセットアップとしてログインしておく必要があります。

スペック内でログイン処理を記述しても動作はしますが、ログイン処理は頻繁に行うので、今のうちにヘルパーを作っておくのがマストでしょう。

```
#各々のアプリに合わせたログイン処理
def sign_in(user)
  cookies[:auth_token] = user.auth_token
end
```

##テスト

Projectsコントローラはログインユーザーとゲストユーザーで処理結果が異なるため、複数のテストシナリオを用意します。

```spec/controllers/projects_controller_spec.rb
require 'rails_helper'

RSpec.describe ProjectsController, type: :controller do
  de
  describe "#index" do

    context "guest" do
      it "returns a 302 response" do
        get :index
        expect(response).to have_http_status "302"
      end

      it "redirects to the sign_in page" do
        get :index
        expect(response).to redirect_to "users/sign_in"
      end
    end

    context "logged_in" do
      before do
        @user = FactoryBot.create(:user)
      end
    
      it "responds successfully" do
        sign_in @user
        get :index
        expect(response).to be_success
      end

      it "responds a 200 response" do
        sign_in @user
        get :index
        expect(response).to have_http_status "200"
      end
    end
  end
end
```

#ユーザーの入力をテスト
createアクションを例にPOST, PATCH, DESTROYリクエストもテストしていきます。
入力のエラーもテストも忘れないようにしましょう。

```spec/controllers/projects_controller_spec.rb
describe "#create" do
  context "guest" do
    it "returns a 302 response" do
      project_params = FactoryBot.attributes_for(:project)
      post :create, params: { project: project_params }
      expect(response).to have_http_status "302"
    end

    it "redirects to the sign_in page" do
      project_params = FactoryBot.attributes_for(:project)
      post :create, params: { project: project_params }
      expect(response).to redirect_to "/users/sign_in"
    end
  end

  context "logged_in" do
    before do
      @user = FactoryBot.create(:user)
    end

    context "vaild attributes" do
      it "adds a project" do
        project_params = FactoryBot.attributes_for(:project)
        sign_in(@user)
        expect{
          post :create, params: { project: project_params }
        }.to change(@user.projects, :count).by(1)
      end
    end

    context "invaild attributes" do
      it "dose not add a project" do
        project_params = FactoryBot.attributes_for(:project, :invalid)
        sign_in(@user)
        expect{
          post :create, params: { project: project_params }
        }.to_not change(@user.projects, :count)
      end
    end
  end
end
```

#HTML以外の出力を扱う
CSVやJSONといったフォーマットでデータを返す処理のテストも見ていきます。
リクエストを送るコードで

```
format: :json
```

でフォーマットを指定し、

```
expect(response.content_type).to eq "application/json"
```
で帰ってきたデータのフォーマットを指定することでテストできます。

[続き](https://qiita.com/zeisho/items/3830b1e47e2b15b7a8af)