RSpecのfeatureテストでsessionを扱う方法

## 環境
- macOS 10.14.5
- ruby 2.5.5
- rails 5.2.3

## はじめに
RailsチュートリアルのテストをRSpecで書いていた時にあることに気づきました。

**featureテストではsessionが使えない!?**

[Railsチュートリアル8章](https://railstutorial.jp/chapters/basic_login?version=5.1#cha-basic_login)のテストでis_logged_in?というユーザーがログイン中かどうかを論理値で返すテスト用ヘルパーを紹介していました。


```ruby:test/test_helper.rb
# テストユーザーがログイン中の場合にtrueを返す
  def is_logged_in?
    !session[:user_id].nil?
  end
```
なので同様に`spec/support/test_helper.rb`に記述してfeatureテストでヘルパーを使用した。
(ヘルパーを定義する方法はこちらを見るとよいと思います
→ [マクロ（ヘルパーメソッド）を定義してフィーチャースペックのユーザー切替えを楽に行う
](https://qiita.com/jnchito/items/a8360e5e7a829d1e19b2))

```ruby:spec/support/test_helper.rb
module TestHelper
  def is_logged_in?
    !session[:user_id].nil?
  end
end
```
```ruby:spec/features/users_signup_spec.rb
require 'rails_helper'

RSpec.feature "UsersSignups", type: :feature do

  feature "valid signup information" do
    before do
      visit signup_path
      fill_in "Name", with: "Example User"
      fill_in "Email", with: "user@example.com"
      fill_in "Password", with: "password"
      fill_in "Confirmation", with: "password"
    end

    scenario "add users count" do
      expect {
        click_button "Create my account"
        expect(page).to have_current_path user_path(User.last)
      }.to change(User, :count).by(1)
    end

    scenario "show flash message" do
      click_button "Create my account"
      expect(page).to have_content "Welcome to the Sample App!"
      visit current_path
      expect(page).to_not have_content "Welcome to the Sample App!"
    end

    scenario "login created user" do
      click_button "Create my account"
      expect(is_logged_in?).to be_truthy  #### ここで使用 ####
    end
  end

  feature "invalid signup information" do
    before do
      visit signup_path
      fill_in "Name", with: ""
      fill_in "Email", with: "user@invalid"
      fill_in "Password", with: "foo"
      fill_in "Confirmation", with: "bar"
    end

    scenario "no difference users count" do
      expect {
        click_button "Create my account"
        expect(page).to have_current_path signup_path
      }.to_not change(User, :count)
    end

    scenario "is show error messages" do
      click_button "Create my account"
      expect(page).to have_content "Name can't be blank"
      expect(page).to have_content "Email is invalid"
      expect(page).to have_content "Password is too short"
      expect(page).to have_content "Password confirmation doesn't match Password"
    end
  end
end

```

するとこんなエラーが、、、

```
1) UsersSignups valid signup information login created user
     Failure/Error: !session[:user_id].nil?
     
     NameError:
       undefined local variable or method `session' for #<RSpec::ExampleGroups::UsersSignups::ValidSignupInformation:0x00007fe38a4a3e20>
     # ./spec/support/login_macros.rb:3:in `is_logged_in?'
     # ./spec/features/users_signup_spec.rb:30:in `block (3 levels) in <top (required)>'
     # -e:1:in `<main>'

```

## 解決策
どうやら調べてみるとfeatureテスト内ではsesssionが扱えないようで、コントローラーテスト内では使えるみたい。
でもどうにかfeatureテスト内でこのヘルパーを使えるようにする方法はないかと調べていたところある記事を見つけました。
[Rails feature testing with logged in user (undefined method 'session')
](https://stackoverflow.com/questions/20483314/rails-feature-testing-with-logged-in-user-undefined-method-session)

`'rack_session_access'`というgemを使うとfeatureテスト内でもsessionを参照したり、変更したりできるみたいなので[gemのREADME](https://github.com/railsware/rack_session_access)を参考にセットアップしました。

```ruby:Gemfile
gem 'rack_session_access' # 追加
```
```
$ bundle install   # gemをインストール
```
```ruby:config/environments/test.rb
Rails.application.configure do
  :
  :
  config.middleware.use RackSessionAccess::Middleware  # 追加
end
```
```ruby:spec/spec_helper.rb
require "rack_session_access/capybara"   # 追加
```
このgemを使うとsessionに値を挿入することもできるらしいが今回は値が入っているかどうかを調べたいだけなので以下のメソッドを記述してRailsチュートリアルに習って論理値を反転。

```ruby:spec/support/test_helper.rb
module TestHelper
  def is_logged_in?
    !page.get_rack_session_key('user_id').nil?   # 変更
  end
end
```
はじめは`get_rack_session_key(:user_id)`と修正したがダメだったのでREADEMEを見ると、文字列(`'user_id'`)を渡さなきゃいけないらしい

## まとめ
`'rack_session_access'`gemを使うとfeatureスペックでもsessionを扱える！

しかし頻繁にsessionの操作をfeatureスペック内で行わないようなら、ログインしているかどうかをテストする方法は他にもいくらでもあるのでgemをわざわざ入れる必要はないかも。。