【Ruby on Rails】RSpecでのコントローラーテスト

# 開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina

# 前提
こちらが出来ている前提で進めます。
[【Ruby on Rails】RSpec導入まで](https://qiita.com/japwork/items/2a499c5f09cd53838d84)

※　▶◯◯　を選択すると、説明等が出てきますので、
　　よくわからない場合の参考にしていただければと思います。



モデルのテストはこちら
[【Ruby on Rails】RSpecでのモデルテスト](https://qiita.com/japwork/items/c6f58bae5e0b222e1eb0)

# コントローラーのテスト準備
##・アクションの作成
今回はユーザーがログインした時だけ、
表示されるかどうかをテストします。

```rb:app/controllers/posts_controller.rb
class Admins::PostsController < ApplicationController
  before_action :authenticate_user!
  def index
    @posts = Post.all
  end
end
```
<details><summary>補足【before_action :authenticate_user!】</summary>
gem deviseのヘルパーメソッドです。</details>



##・ファイルの作成
①
spec配下にrequestsフォルダとfactoriesフォルダを作成。
requestsフォルダには、テストしたいcontrollerのファイルも作成し、
factoriesフォルダにはダミーデータのモデルを作成します。

今回はpostコントローラーをテストするため、
下記のようなファイル構成にします。

```
spec/requests/posts_request_spec.rb
　→テストしたい内容を記述します。

spec/factories/post.rb
spec/factories/admin.rb
　→ダミーデータを作成します。
```

②
FactoryBotを使えるようにします。
使用するとuser = create(:user) のようにDB登録やモデルのビルドができるため便利です。
spec配下にsupportフォルダとfactory_bot.rbファイルを作成し、下記のように記述します。

```rb:spec/support/factory_bot.rb
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end
```
その後、下記を追加します。

```rb:spec/rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../config/environment', __dir__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
require 'support/factory_bot' # ＜ーーー　追加

...

```

## rails_helper.rbの編集
下記内容を```RSpec.configure do |config|```内に追加します。
※この記述により、sign_inメソッドが使用できるようになります。

```rb:spec/rails_helper.rb
RSpec.configure do |config|

...

  config.include Devise::Test::IntegrationHelpers, type: :request # <--追加
end
```
# 実際のコード
まずはダミーデータを作成します。

```rb:spec/factories/admin.rb
FactoryBot.define do
  factory :admin do
    email { Faker::Internet.email }
    phone_number { 12345678909 }
    password { 'password' }
    password_confirmation { 'password' }
  end
end
```
```rb:spec/factories/post.rb
FactoryBot.define do
  factory :post do
    body { Faker::Lorem.characters(number:20) }
    admin
  end
end
```

次にテストコードを記述します。

```rb:spec/requests/posts_request_spec.rb
require 'rails_helper'

RSpec.describe "postコントローラーのテスト", type: :request do
  let(:admin) { create(:admin) }
  let(:post) { create(:post) }
  describe 'ログイン済み' do
    before do
      sign_in admin
    end
    context "投稿一覧ページが正しく表示される" do
      before do
        get admins_posts_path
      end
      it 'リクエストは200 OKとなること' do
        expect(response.status).to eq 200
      end
      it 'タイトルが正しく表示されていること' do
        expect(response.body).to include("投稿一覧")
      end
    end
  end
  describe '非ログイン' do
    context "投稿一覧ページへ遷移されない" do
      before do
        get admins_posts_path(admin)
      end
      it 'リクエストは401 OKとなること' do
        expect(response.status).to eq 401
      end
    end
  end


  # get admins_posts_path(admin)であればパスワードが違うため401エラーであり、
  # get admins_posts_pathであれば302のリダイレクトになるため、テストしたい方で記述してください。

  # describe '非ログイン' do
  #   context "投稿一覧ページへ遷移されない" do
  #     before do
  #       get admins_posts_path
  #     end
  #     it 'リクエストは302 OKとなること' do
  #       expect(response.status).to eq 302
  #     end
  #   end
  # end
end
```

その後、ターミナルで下記を実行してください。

```:ターミナル
$ rspec spec/requests
```
テストを通過すると

```
Finished in 3.64 seconds (files took 2.75 seconds to load)
3 examples, 0 failures
```

このように表示されるためテスト内容が正しいことを表しています。

## 参考（モデルのテストから抜粋）
[【Ruby on Rails】RSpecでのモデルテスト](https://qiita.com/japwork/items/c6f58bae5e0b222e1eb0)
逆にテストを通過しない場合、このような形でどこでエラーが起きているかわかるので、
テストコードが間違っているのか、バリデーションが間違っているかなどがわかるようになります。

```
Failures:

  1) Postモデルのテスト バリデーションのテスト titleカラム 20文字以下であること
     Failure/Error: let!(:post) { build(:post) }

     NoMethodError:
       undefined method `build' for #<RSpec::ExampleGroups::Post::Nested::Title:0x000000000619e938>
     # ./spec/models/post_spec.rb:9:in `block (3 levels) in <top (required)>'

  2) Postモデルのテスト バリデーションのテスト titleカラム 空欄でないこと
     Failure/Error: let!(:post) { build(:post) }

     NoMethodError:
       undefined method `build' for #<RSpec::ExampleGroups::Post::Nested::Title:0x0000000007491518>
     # ./spec/models/post_spec.rb:9:in `block (3 levels) in <top (required)>'
Finished in 0.07992 seconds (files took 2.41 seconds to load)
2 examples, 2 failures

Failed examples:

rspec ./spec/models/post_spec.rb:11 # Postモデルのテスト バリデーションのテスト titleカラム 20文字以下であること
rspec ./spec/models/post_spec.rb:15 # Postモデルのテスト バリデーションのテスト titleカラム 空欄でないこと
```

また下部にあるrspec ./spec/models/post_spec.rb:11を使い、
下記のように個別にテスト内容を確認することも出来ます。

```:ターミナル
$ rspec spec/models/post_spec.rb:11
```

# まとめ
今回は
１，ログインした状態でpost一覧画面が正しく表示されるか
２，ログインした状態でタイトルが正しく表示されているか
３，非ログインであれば、閲覧することができないか

この3点をテストしました。

フォルダ名のrequestsは
要求したものに対して、正しく応答しているかをテストするため、requestsになっています。
そのため要求するものが違えば、401であったり302のエラーを返すことになるので、
表示させたいエラーによって記述してください。

またtwitterではQiitaにはアップしていない技術や考え方もアップしていますので、
よければフォローして頂けると嬉しいです。
詳しくはこちら　https://twitter.com/japwork