LoginSignup
0
0

More than 1 year has passed since last update.

【RSpec】Railsチュートリアル第6版 第8章

Last updated at Posted at 2021-10-31

はじめに

Railsチュートリアル第6版のテストをRSpecで書き直していく。

目次

事前準備

Gemfileにテストデータ作成用のFactory Botを追加

Gemfile
group :development, :test do
  gem "factory_bot_rails"
end

Minitest

Sessionsコントローラのテスト

test/controllers/sessions_controller_test.rb
require 'test_helper'

class SessionsControllerTest < ActionDispatch::IntegrationTest

  test "should get new" do
    get login_path
    assert_response :success
  end
end

ユーザーログイン処理に対するテスト

test/integration/users_login_test.rb
require 'test_helper'

class UsersLoginTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "login with valid email/invalid password" do
    get login_path
    assert_template 'sessions/new'
    post login_path, params: { session: { email:    @user.email,
                                          password: "invalid" } }
    assert_not is_logged_in?
    assert_template 'sessions/new'
    assert_not flash.empty?
    get root_path
    assert flash.empty?
  end

  test "login with valid information followed by logout" do
    get login_path
    post login_path, params: { session: { email:    @user.email,
                                          password: 'password' } }
    assert is_logged_in?
    assert_redirected_to @user
    follow_redirect!
    assert_template 'users/show'
    assert_select "a[href=?]", login_path, count: 0
    assert_select "a[href=?]", logout_path
    assert_select "a[href=?]", user_path(@user)
    delete logout_path
    assert_not is_logged_in?
    assert_redirected_to root_url
    follow_redirect!
    assert_select "a[href=?]", login_path
    assert_select "a[href=?]", logout_path,      count: 0
    assert_select "a[href=?]", user_path(@user), count: 0
  end
end

ユーザーログインのテストで使うfixture

test/fixtures/users.yml
michael:
  name: Michael Example
  email: michael@example.com
  password_digest: <%= User.digest('password') %>

テスト中のログインステータスを論理値で返すメソッド

test/test_helper.rb
ENV['RAILS_ENV'] ||= 'test'
.
.
.
class ActiveSupport::TestCase
  fixtures :all

  def is_logged_in?
    !session[:user_id].nil?
  end
end

ユーザー登録後のログインのテスト

test/integration/users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest
  .
  .
  .
  test "valid signup information" do
    get signup_path
    assert_difference 'User.count', 1 do
      post users_path, params: { user: { name:  "Example User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
    end
    follow_redirect!
    assert_template 'users/show'
    assert is_logged_in?
  end
end

RSpec

Sessionsコントローラのテスト

spec/requests/sessions_spec.rb
require 'rails_helper'

RSpec.describe "Sessions", type: :request do

  it "returns http success" do
    get login_path
    expect(response).to have_http_status(:success)
  end

end

ユーザーログイン処理に対するテスト

spec/system/users_login_spec.rb
require 'rails_helper'

RSpec.describe "UsersLogins", type: :system do

  let(:user) {FactoryBot.create(:user)}

  it "login with valid email/invalid password" do
    visit login_path
    expect(page).to have_current_path login_path
    fill_in "Email", with: user.email
    fill_in "Password", with: "invalid"
    click_button "Log in"
    expect(page).to have_current_path login_path
    expect(page).to have_content("Invalid email/password combination")
    visit root_path
    expect(page).to_not have_content("Invalid email/password combination")
  end

  it "login with valid information followed by logout" do
    visit login_path
    expect(page).to have_current_path login_path
    fill_in "Email", with: user.email
    fill_in "Password", with: user.password
    click_button "Log in"
    expect(page).to have_current_path user_path(user)
    expect(page).to_not have_link href: login_path
    expect(page).to have_link href: logout_path
    expect(page).to have_link href: user_path(user)
    visit root_path
    click_on "Log out"
    expect(page).to have_current_path root_path
    expect(page).to have_link href: login_path
    expect(page).to_not have_link href: logout_path
    expect(page).to_not have_link href: user_path(user)
  end

end

system specにおいて、sesssion機能は使用できないらしいので、is_logged_in?メソッドはrequest specにて別途記述する。

ユーザーログインのテストで使うfactory

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name {"Michael Example"}
    sequence(:email) { |n| "test#{n}@example.com" } 
    password {"password"}
    password_confirmation  {"password"}
  end
end

emailにシーケンスを使用し、ユニークで連続したメールアドレスを作成。

テスト中のログインステータスを論理値で返すメソッド

spec/support/support.rb
module Support
  def is_logged_in?
    !session[:user_id].nil?
  end
end

RSpec.configure do |config|
  config.include Support
end

spec/supportディレクトリにサポートモジュールを作成し、is_logged_in?を追加。RSpec.configureでサポートモジュールをincludeする。

is_logged_in?(ログイン時)

spec/requests/users_session_spec.rb
require 'rails_helper'

RSpec.describe "UsersSessions", type: :request do

  let(:user) {FactoryBot.create(:user)}

  it "log in and log out" do
    post login_path, params: { session: {
      email: user.email,
      password: user.password,
    } }
    expect(response).to redirect_to user_path(user)
    expect(is_logged_in?).to be_truthy 
    delete logout_path
    expect(response).to redirect_to root_path
    expect(is_logged_in?).to be_falsey
  end

end

ユーザー登録後のログインのテスト

spec/requests/users_signups_spec.rb
require 'rails_helper'

RSpec.describe "UsersSignups", type: :request do

  let(:user) { FactoryBot.attributes_for(:user) }

  it "valid signup information" do
    expect {
      post users_path, params: { user: user }
    }.to change(User, :count).by(1)
    expect(response).to redirect_to user_path(User.last)
    expect(is_logged_in?).to be_truthy
  end

end

ユーザーログイン処理に対するテストと同じく、system specにおいて、sesssion機能は使用できないので、request specを別途作成。

発生するエラーについて(解決済み)

ユーザーログイン処理に対するテストの中のexpect(page).to have_current_path "/"のテストにおいて、
Failure/Error: expect(page).to have_current_path "/" expected "/logout" to equal "/"というエラーが発生する。

また、is_logged_in?の中のexpect(response).to redirect_to user_path(@user)のテストにおいて、
Failure/Error: expect(response).to redirect_to user_path(@user)
Expected response to be a <3XX: redirect>, but was a <422: Unprocessable Entity>

というエラーが発生する。

解決策

spec/rails_helper.rb内の

spec/rails.helper.rb
ENV['RAILS_ENV'] ||= 'test'

spec/rails.helper.rb
ENV['RAILS_ENV'] = 'test'

に変更する。

RSpecを作成した際、ENV['RAILS_ENV'] ||= 'test'がデフォルトで作成され、通常はRSpecの環境変数にはtestが適用されるはずが、なぜかRails.env="development"になっており、エラーが発生していた。
これにより、RSpecのデータベースに開発環境用のデータベースが使用されており、また通常テスト環境には適用されないCSRFチェックが働き、422エラーなどを発生させていた。
かなりハマってしまったので是非参考にしていただきたい。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0