【Rspec】requests spec・コントローラーのテストについて〜adminユーザー作成やeditアクションのテスト〜

### requests spec・コントローラーのテストについての話
モデルと結合テストは問題なく終わり、コントローラーのテストについてあれこれ調べていたがなかなか自分に理解できるものがなく、困っていました。
公開されているGitHubのコードを見てもコントローラーのテストコードをやっている人が少なかったというのも一つの要因なのですが、試行錯誤してなんとか完成しました。
正直これでいいのかちょっとわからない部分もあるので、もし知見がある方がこの記事を見て下さったらコメント頂けると嬉しいです。


### adminユーザーの生成で困ったこと
DBにadminカラムを作って`true`か`false`かで判別しています。
FactoryBotでユーザーを生成するために書いたコードが下記のものです。

```spec/factories/users.rb
FactoryBot.define do
  factory :user do
    nickname              { '野比のび太' }
    sequence(:email)      { |n| "tester#{n}@example.com" }
    password              { 'q11111' }
    password_confirmation { password }
    profile               { 'なんとかしてよドラえもん' }
    admin                 { false }　　　　⬅️一般ユーザー
  end

  factory :admin_user do
    nickname              { 'ドラえもん' }
    sequence(:email)      { |n| "tester#{n}@example.com" }
    password              { 'a11111' }
    password_confirmation { password }
    profile               { 'いつまでも子供じゃないんだよしっかりしろよ' }
    admin                 { true }　　　　　⬅️adminユーザー
  end
end
```

そしてusers_spec.rbには以下のように記述。


```spec/requests/users_spec.rb

RSpec.describe UsersController, type: :request do
  let(:user) { FactoryBot.create(:user) }
  let(:admin_user) { FactoryBot.create(:user, admin: true) }
 #let(:admin_user) { FactoryBot.create(:admin_user) }⬅️この書き方でもダメでした
```

これでadminユーザーが作成されるはずだと思っていたのですが`binding.pry`で止めて中身を見るとadmin_userが作成されていませんでした。
色々書き方を変えてやってみたところ、`let`を使わずに`before do`にしたらうまくいきました。

```spec/requests/users_spec.rb
RSpec.describe UsersController, type: :request do
  before do
    @user = FactoryBot.create(:user)
    @admin_user = FactoryBot.create(:user, admin: true)
   #@admin_user = FactoryBot.create(:admin_user)⬅️この書き方でもダメでした
  end
```

![3c9fb0574c103d376e614707e02c1126.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/887559/f60356d2-408f-4e70-45bc-72a5f0869dfa.png)


ひとまずこれで一般ユーザーとadminユーザーを分けることができるようになりました。


### editアクションのテストで困ったこと
コントローラーのテストをする上でまず困ったのがeditアクションのテストでした。
`authenticate_user!`を使用しているためコントローラーのテスト実行時に、
「ログインしていないユーザー」とみなされてしまってリダイレクトされてしまうのです。
リクエストが正常のレスポンスではなくなってしまうと、テスト項目の
`expect(response.status).to eq 200`
ここの200が302になってしまいます。

また、編集できる時とできない時、両方のテストをしなければいけないため、コントローラーのテストコード上でログインをさせることができれば正常な返り値が得られると考えました。


### deviseのメソッドをテストコード上でも使えるようにするには

テストコード上でログインさせるメソッドとして結合テストコード時に使っていたmoduleでやってみたところ、うまくいきませんでした。
調べた結果、コントローラーのテストではdeviseのメソッドを使用できるとのことだったのでそちらに切り替えました。


```spec/rails_helper.rb
RSpec.configure do |config|
  #deviseのsign_inメソッドが使えるようになる
  config.include Devise::Test::IntegrationHelpers, type: :request
end
```
これで`sign_in`というメソッドが使用できるようになりました。
自分の場合は`@user`と定義しているのでそのまま使います。


```spec/requests/users_spec.rb
require 'rails_helper'
RSpec.describe UsersController, type: :request do
  before do
    @user = FactoryBot.create(:user)
  end

  describe 'GET #edit' do
    context 'userがログインしているとき' do
      before do
        sign_in @user　　⬅️ここでログイン
      end
      it 'editアクションにリクエストすると正常にレスポンスが返ってくる' do
        get edit_user_path(@user)
        expect(response.status).to eq 200　　⬅️編集画面に遷移できている
      end
      it "editアクションにリクエストするとレスポンスに登録済みuserの名前が存在する" do 
        get edit_user_path(@user)
        expect(response.body).to include @user.nickname
      end
      it "editアクションにリクエストするとレスポンスに登録済みuserのプロフィールが存在する" do 
        get edit_user_path(@user)
        expect(response.body).to include @user.profile
      end
    end

    context 'userがログインしていないとき' do
      it 'editアクションにリクエストすると正常にレスポンスが返ってくる' do
        get edit_user_path(@user)
        expect(response.status).to eq 302　　⬅️トップページにリダイレクトしている
      end
    end
  end
end

```
これでうまくいきました。
admin_userにしたいときはbefore doで`@admin_user`を定義して、
`sign_in @admin_user`とすることで他のコントローラーでも問題なく処理ができました。
テストコードをやるとアプリケーションの仕様がよく理解できると言いますが、理由がよくわかりました。
細かいところに気がついたり、仕様そのものを一から見直す結果になって理解が深まったと実感できました。


### 参考にさせていただきました

@iwkmsy9618様
[[Rails]Request Specでのログインの実施](https://qiita.com/iwkmsy9618/items/d097f0f315b93049c6c5)

ありがとうございました。