【Rails7テスト】ログインからログアウトまでの一連の動きをテストする方法(ユーザー情報はfixtureで作成)

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 使用)

ソースコード

GitHub
GitHub - hirokirokki0820/user-authentication Contribute to hirokirokki0820/user-authentication development by creating an account on GitHub.

ログインしてからログアウトするまでのテストを作成する流れ

ここでは、すでに基本的なログイン、ログアウト機構はすでに完成しているというていで話を進めていきます。

あわせて読みたい
【Rails7】ユーザー認証機能(ログイン・ログアウト)を実装するまでの一連の流れ Railsでサービスを開発しようとなると避けて通れない(と思う)のが、ユーザーの認証機能。 認証機能を1から作るのはかなり大変ですが、幸いRailsにはDeviceという簡単...

まずは、今回「ログイン〜ログアウトまでの一連の動作を確認するテスト」の全体像(コード)です。

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

この一連の流れを

  1. ログイン失敗時のテスト
  2. ログイン成功時のテスト
  3. ログアウト時のテスト

の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?)
  • トップページにリダイレクトする
  • トップページが表示される
  • 表示されたページ(ナビメニューなど)にログインリンクが表示されている
  • 表示されたページ(ナビメニューなど)にログアウトリンクが表示されいない
  • 表示されたページ(ナビメニューなど)にマイページ用リンクが表示されていない

これらがちゃんと機能しているかどうかをみています。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

愛知の34歳。ブロガー&フリーター(現在無職)。院卒で大手自動車部品メーカーに入社するも、仕事が嫌になり3年で退職。28歳でNZワーホリへ。帰国後から現在まで、ワーホリのような「働きたい時だけ働く」「嫌なことはしない」という生き方しています。ここ1年は無職。趣味は登山、旅、音ゲー(ギタフリ)、たまにプログラミング。

コメント

コメントする

目次