Railsで作成した基本的なログイン、ログアウト機構のテストをする方法について。
ログイン、ログアウト機構ではsessionsコントローラーに対応するモデルが存在しないので、
ユーザー新規作成時のテストとは手順が少し異なるようでした。
今後もまた必要となると思うので、自分用のメモとして残しておこうと思います。
(テストコードのフレームワークはminitestを使うものとします)
それではさっそく参ります。
開発環境
- Ruby 3.1.2
- Ruby on Rails 7.0.3
- M1 Macbook Air 2020
- mac OS Monterey (ver. 12.4)
- ターミナル bash (Rosetta 2 使用)
ソースコード
ログインしてからログアウトするまでのテストを作成する流れ
ここでは、すでに基本的なログイン、ログアウト機構はすでに完成しているというていで話を進めていきます。

まずは、今回「ログイン〜ログアウトまでの一連の動作を確認するテスト」の全体像(コード)です。
require "test_helper"
class UsersLoginTest < ActionDispatch::IntegrationTest
# fixture(users.yml)で作成したテスト用データーをセットする(ユーザー: Tom)
def setup
@user = users(:Tom)
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
assert_template "sessions/new"
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_path
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
テストは以下のコマンドで実行します。
# 全体のテストを実行する場合(モデル、コントローラー含む)
$ rails test
# インテグレーションフォルダ内のテストを実行する場合
$ rails test:integration
# インテグレーションフォルダ内の特定のテストを実行する場合
$ rails test test/integration/users_login_test.rb
この一連の流れを
- ログイン失敗時のテスト
- ログイン成功時のテスト
- ログアウト時のテスト
の3段階に分けて見ていきましょう。
テスト用のユーザー情報をfixture(フィクスチャ)で作成する
まずテストを始める前に、テスト用のユーザー情報を登録します。
テスト用のユーザー情報はfixtureで作成。
このfixtureを使うことで、テストに必要なデータをtestデータベースに読み込んでおくことができます。
まずは、fixture向けのdigestメソッドをユーザーモデルuser.rb
の最下部に追加します。
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
ちなみに、Railsのsecure_passwordのソースコードを調べてみると、
BCrypt::Password.create(string, cost: cost)
の部分でハッシュ化されたパスワードが生成されていることが分かります。
続いて、test/fixtures/users.yml
にテスト用のユーザー情報を書いていきます。
(複数名の情報を登録しても良いのですが、今回は一人で十分です)
Tom:
name: Tom Example
email: takeshi@example.com
password_digest: <%= User.digest('password') %>
この<%= User.digest('password') %>
という部分でユーザーのパスワード(文字列 ‘password’ を指定)をハッシュ化しています。
これで、Tomというユーザー情報がデータベースに登録されている、という状態を作ることができました。
ログイン失敗時のテスト
ログイン失敗時(情報の未入力)のテストは、以下のように割とシンプルに書けます。
# ログイン失敗時のテスト
test "login with invalid information" do
get login_path
assert_template "sessions/new"
post login_path, params:
{ session: {
email: "",
password: "" } }
assert_not is_logged_in?
assert_template "sessions/new"
assert_not flash.empty?
get root_path
assert flash.empty?
end
ログイン失敗時のテストの流れとしては、
- ログインページにアクセスする
- ログインページが表示される
- メールアドレス、パスワードを未入力のままPOSTする
- ログインできない
- 「ログインに失敗しました」等のフラッシュメッセージが表示される
- 再びログインページが表示される
- トップページにアクセスする
- フラッシュメッセージが消える
これらがちゃんと機能しているかどうかをみています。
ただ、ログイン失敗には「メールアドレスは合っているが、パスワードが間違っている」というパターンもあるわけなので、
上記のテストだけでは不十分です。
そこで必要になるのが、先ほどtest/fixtures/users.yml
で作成しておいたテスト用のユーザー情報です。
このテスト用のユーザーを事前にセットアップしてから、コードを以下のように書き換えます。
require "test_helper"
class UsersLoginTest < ActionDispatch::IntegrationTest
# fixture(users.yml)で作成したテスト用データーをセットする(ユーザー: Tom)
def setup
@user = users(:Tom)
end
# ログイン失敗時のテスト
test "login with valid email/invalid password" do
get login_path
assert_template "sessions/new"
# ユーザー "Tom" のメールアドレスを使用し、間違ったパスワードを入力してテスト
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
end
これで、「メールアドレスは合っているが、パスワードが間違っていてログインに失敗する」というテストが完成しました。
以下のコマンドを実行するとテストを開始します。
$ rails test test/integration/users_login_test.rb
ちなみに、テストコード中のis_logged_in?
メソッドは、test/test_herper.rb
内で定義した、テストユーザーがログイン中の場合にtrueを返すメソッドです。
assert_not
の期待値はfalse
、つまりis_logged_in?
がfalse
(ログインできていない)になればテストはパスということです。
ログイン成功時のテスト
ログイン成功時のテストは以下のように書けます。
test "login with valid information" do
get login_path
assert_template "sessions/new"
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)
end
$ rails test test/integration/users_login_test.rb
ログイン成功時のテストの流れとしては、
- ログインページにアクセスする
- ログインページが表示される
- 有効なメールアドレス、パスワードを入力しPOSTする
- ログインに成功する
- ユーザーのマイページにリダイレクトされる
- ユーザーのマイページが表示される
- 表示されたページ(ナビメニューなど)にログインリンクが表示されていない
- 表示されたページ(ナビメニューなど)にログアウトリンクが表示されている
- 表示されたページ(ナビメニューなど)にマイページ用リンクが表示されている
これらがちゃんと機能しているかどうかをみています。
続いては、ログアウト時のテストも付け加えていきます。
ログアウト時のテスト(ログイン成功時のテストに付け加える)
先ほどの、ログイン時のテストに付け加える形で書いていきます。
test "login with valid information followed by logout" do
get login_path
assert_template "sessions/new"
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_path
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
$ rails test test/integration/users_login_test.rb
ログアウト時のテストの流れとしては、
- ログアウトを実行する(セッション情報を削除する)
- ログインしていない状態であることを確認する(assert_not is_logged_in?)
- トップページにリダイレクトする
- トップページが表示される
- 表示されたページ(ナビメニューなど)にログインリンクが表示されている
- 表示されたページ(ナビメニューなど)にログアウトリンクが表示されいない
- 表示されたページ(ナビメニューなど)にマイページ用リンクが表示されていない
これらがちゃんと機能しているかどうかをみています。
コメント