#はじめに
Railsチュートリアル第6版のテストをRSpecで書き直していく。
###目次
#事前準備
Gemfileにテストデータ作成用のFactory Botを追加
group :development, :test do
gem "factory_bot_rails"
end
#Minitest
###Sessionsコントローラのテスト
require 'test_helper'
class SessionsControllerTest < ActionDispatch::IntegrationTest
test "should get new" do
get login_path
assert_response :success
end
end
###ユーザーログイン処理に対するテスト
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
michael:
name: Michael Example
email: michael@example.com
password_digest: <%= User.digest('password') %>
###テスト中のログインステータスを論理値で返すメソッド
ENV['RAILS_ENV'] ||= 'test'
.
.
.
class ActiveSupport::TestCase
fixtures :all
def is_logged_in?
!session[:user_id].nil?
end
end
###ユーザー登録後のログインのテスト
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コントローラのテスト
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
###ユーザーログイン処理に対するテスト
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
FactoryBot.define do
factory :user do
name {"Michael Example"}
sequence(:email) { |n| "test#{n}@example.com" }
password {"password"}
password_confirmation {"password"}
end
end
emailにシーケンスを使用し、ユニークで連続したメールアドレスを作成。
###テスト中のログインステータスを論理値で返すメソッド
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?(ログイン時)
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
###ユーザー登録後のログインのテスト
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内の
ENV['RAILS_ENV'] ||= 'test'
を
ENV['RAILS_ENV'] = 'test'
に変更する。
RSpecを作成した際、ENV['RAILS_ENV'] ||= 'test'
がデフォルトで作成され、通常はRSpecの環境変数にはtestが適用されるはずが、なぜかRails.env="development"になっており、エラーが発生していた。
これにより、RSpecのデータベースに開発環境用のデータベースが使用されており、また通常テスト環境には適用されないCSRFチェックが働き、422エラーなどを発生させていた。
かなりハマってしまったので是非参考にしていただきたい。