Edited at

Rails Tutorialの知識から【ポートフォリオ】を作って勉強する話 #8 ログイン/ログアウト, FactroyBot編


こんな人におすすめ


  • プログラミング初心者でポートフォリオの作り方が分からない

  • Rails Tutorialをやってみたが理解することが難しい

  • ポートフォリオを作成しながら勉強したい

前回:#7 ログイン下準備編

次回:#9 永続セッション, cookie編


こんなことが分かる


  • ログイン/ログアウトの実装方法

  • findとfind_byの違い

  • 〇〇px以上を選択できないBootstrapの対策

  • FactoryBotの使い方

  • System specでsessionが使えないことについて

一緒に勉強しま:bow:

(今回は#7からの続きです。そちらを先に!)

<追記>

RSpecテスト見直しにより、記事を大幅アップデートしました。

TutorialのテストとRSpecを比較したい方は#10.5 RSpecでTutorialのテストを書き直すをご確認ください。


機能としてのログインを実装する

Tutorial 8.2 ログインに突入。

ログイン関連は固まったので、いよいよ機能としてのログインを実装する。

やることとしてはこんな感じ。


  • Sessionsヘルパーにログイン/ログアウト関係のメソッドを4つ定義

  • ログイン中はログアウト、ログアウト中はログインボタンを表示する

  • 登録済みユーザが再びサイトに訪れる際、ログイン済みにする


Sessionsヘルパーにメソッドを定義する

まずはログイン系のメソッドをSessionsヘルパーにまとめよう。

(4つ目のメソッドlog_outはだいぶ後で紹介します)


sessionメソッドに混乱しない

ログインの実装だけど実はsessionメソッドを使用するだけで完了する。

より使いやすいようにlog_inメソッドとして定義しなおす。


app/helpers/sessions_helper.rb

module SessionsHelper

def log_in(user)
session[:user_id] = user.id
end
end


このsessionメソッドだけど、さっき作成したSessionsコントローラやビューとは全く異なる。


  • sessionメソッド → ログインを維持するメソッド

  • Sessionコントローラ/ビュー → webアプリへのログインを促すコントローラ/ビューに対して勝手に命名したもの

詳しくはこちら↓

【Rails入門】sessionの使い方まとめ


findとfind_byの違いに混乱しない

sessionからユーザ情報を取り出すためにcurrent_userメソッドを定義する。

そのためにユーザを探すメソッドを使いたいんだけど...

この2種類、ざっくり解説するとこんな感じ。


find


  • 見つからないとエラーになる

  • id以外で探せない


find_by


  • 見つからないとnilになる

  • id以外でも探せる

ここで大事なのはエラーになるかどうか。

sessionメソッドで一時的に保存されたユーザを探す時、ログアウトしていたら中身は存在しない。じゃあログアウト状態でfindメソッドを使うとエラーが発生する。これは嬉しくない:angry:

よくわかる解説↓

find、find_by、whereの違い

find(42)とfind_by(id: 42)の違いについて


||= に混乱しない

find_byについて分かったらcurrent_userメソッドをつくる。

とりあえず先に書いてしまう。


app/helpers/sessions_helper.rb

  # 中略

def current_user
if session[:user_id]
@current_user ||= User.find_by(id: session[:user_id])
end
end
# 中略

なんじゃこりゃ。こういう時は先輩に頼ろう↓

【Rails】@current_user ||= User.find_by(id: session[:user_id])という書き方について

まずこのコードはすぐ下と等価。

@current_user ||= User.find_by(id: session[:user_id])

@current_user = @current_user || User.find_by(id: session[:user_id])

これはなんとなく分かる。

x += 1 → x = x + 1みたいな代入演算子と同じで、

A ||= 1 → A = A || 1ということ。

じゃあ変数current_userにどっち入れてるの?ってなる。

はじめわからなかったけど、||ってtrueなら処理が終了するのね。

で、||は左から順に判定する。ここで右辺を見ると、

@current_user || User.find_by(id: session[:user_id])

となっているので、

「存在するならそのまま、しないなら探した情報をcurrent_userに入れて」

という式になる。

あとはifと入れ子になってるから、

「もしsessionがある時」っていう前条件が追加される。

よってcurrent_userメソッドは、

def current_user

if session[:user_id]
@current_user ||= User.find_by(id: session[:user_id])
end
end

もしsessionがある時、存在するならそのまま、しないならcurrent_userに入れて

ということになる(初学感)


!current_user.nil?に混乱しない

続いてログインしてるかどうかをtrue/falseで確認するメソッドを書く。

このメソッドのおかげでログイン/ログアウト時のレイアウトが変更できる。


app/helpers/sessions_helper.rb

# 中略

def logged_in?
!current_user.nil?
end
end

浅学がゆえに「なんで『!』いるの。」と思っていたのでメモ。

もし「!」がないと...

current_user.nil?

「current_userがnilだったらtrue、nilじゃないならfalse」

これだと人間非常に困る。

current_userいないのにtrue、いるのにfalseになっちゃう。

だから「!」をつけて反転させるわけでした。


ログイン/ログアウト時のレイアウトを変更する

以上のSessionsヘルパーによりログイン時にログアウトボタン、ログアウト時にログインボタンを表示することが可能になった。じゃあそれを実装だ。

TutorialとBootstrapの書き方が異なるので、Navbar - Bootstrap(公式)を確認しながら行おう。

スマホ画面時にボタンの余白がなかったのでアレンジ。ひとまずこんな感じ。


app/views/layouts/_header.html.erb

<header>

<div class="container">
<nav class="navbar navbar-expand-md navbar-dark fixed-top navbar-extend">
<a class="navbar-brand" herf="#">
<div class="navbar-brand-extend">
<%= link_to image_tag('lantern_lantern_logo.png', width: 50), root_path, class: "logo-img" %>
<%= link_to image_tag('lantern_lantern_text.png', height: 30), root_path, class: "logo-text" %>
</div>
</a>
<button class="navbar-toggler" data-toggle="collapse" data-target="#menu">
<span class="navbar-toggler-icon"></span>
</button>
<div id="menu" class="collapse navbar-collapse">
<ul class="navbar-nav navbar-nav-extend ml-auto">
<% if logged_in? %>
<li class="nav-item nav-item-extend"><%= link_to "Lantern画面へ", '#', class: "btn btn-info btn-md btn-extend" %></li>
<li class="nav-item nav-item-extend"><%= link_to "プロフィール", current_user, class: "btn btn-secondary btn-md btn-extend" %></li>
<li class="nav-item nav-item-extend"><%= link_to "ログアウト", logout_path, method: :delete, class: "btn btn-danger btn-md btn-extend btn-logout-extend" %></li>
<% else %>
<li class="nav-item nav-item-extend"><%= link_to "新しくはじめる", signup_path, class: "btn btn-info btn-md btn-extend" %></li>
<li class="nav-item nav-item-extend"><%= link_to "ログイン", login_path, class: "btn btn-info btn-md btn-extend btn-login-extend" %></li>
<% end %>
</ul>
</div>
</nav>
</div>
</header>


app/assets/stylesheets/application.scss.erb

@import "bootstrap";

// 中略

// max-width = 768px
@media (max-width: 768px) {
.nav-item-extend {
margin-top: 0.6rem;
}
}

// button
.btn-extend {
width: 8.5rem;
}

// header
.navbar-extend {
margin-bottom: 0;
background-color: $lantern-black;
opacity: 0.9;
ul li a button {
padding: 0.375rem 1.5rem;
}
}

.logo-img:hover {
opacity: 0.6;
}

.logo-text {
padding-top: 2px;
:hover {
opacity: 0.6;
}
}

.nav-item-extend {
> a:not(.btn-extend) {
// text-decoration: none;
&:hover {
opacity: 0.6;
}
}
> a {
margin-left: 1rem;
float: right;
}
}

.nav-link {
color: $lantern-dark-white;
}

// 中略


既ログイン時のアプリケーション画面への自動遷移なんかは後々考える。

そういや何気にBootstrapは〇〇px以下の時という指定ができないっぽい。

必要な時はcssで。

@media (max-width: 768px) {

// 処理
}


レイアウトのテストを実装する

Tutorial 8.2.4ではテスト用データの作成にFixturesを使用する。

ポートフォリオではRSpecを使う理由から代わりにFactroyBotを使用する。

というわけでまたまたまた新たに導入。


Gemfile

group :development, :test do

+ gem "factory_bot_rails"
end


bash

$ bundle install


RSpecでFactoryBotの記述が省略できるよう変更。


spec/rails_helper.rb

# 中略

# コメントアウトを外す
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# 中略
RSpec.configure do |config|
# 以下を追加
config.include FactoryBot::Syntax::Methods
# 中略
end

終わったらFactoryBot生成。

何気にジェネレータあるのでそっちを使ってみる。


bash

$ rails g factory_bot:model user



備考:ジェネレータの種類を確認するには

「これってジェネレータあったっけ?」

ジェネレータの種類を一発で確認する方法がある。


bash

$ rails g --help

中略(例えばRSpec)
Rspec:
rspec:controller
rspec:feature
rspec:helper
rspec:install
rspec:integration
rspec:job
rspec:mailer
rspec:model
rspec:observer
rspec:request
rspec:scaffold
rspec:view


分からなくなったら使ってみよう。


...話を戻します。


ログイン時のテストを書く


  • 正しくログインが行えたか → Request spec

  • ログイン/ログアウト時のレイアウトは変更されたか → System spec

この2つのテストを書きます。


正しくログインが行えたかのテスト

真っ先に思いつくのはこう。


spec/requests/users_logins_spec.rb

require 'rails_helper'

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

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

describe "GET /login" do
context "invalid information" do
it "fails having a danger flash message" do
get login_path
post login_path, params: {
session: {
email: "",
password: ""
}
}
expect(flash[:danger]).to be_truthy
expect(logged_in?).to be_falsey
end
end

context "valid information" do
it "succeeds having no danger flash message" do
get login_path
post login_path, params: {
session: {
email: user.email,
password: user.password
}
}
expect(flash[:danger]).to be_falsey
expect(logged_in?).to be_truthy
end
end
end
end


しかしこれだと動きません。

実はこれのせいです。

expect(logged_in?).to be_falsey

expect(logged_in?).to be_truthy

これには2つ問題があります。


1つ目:Sessionsヘルパーが呼び出せていない

ここで評価されるlogged_in?はSessionsヘルパーです。

しかしヘルパーは呼び出すことを宣言しないと使えません。

よってincludeを使って呼び出しましょう。


spec/requests/users_logins_spec.rb

require 'rails_helper'

RSpec.describe "UsersLogins", type: :request do
include SessionsHelper
# 中略



2つ目:クッキー関連のメソッドが呼び出せない

それでも失敗します。

logged_in?はcurrent_userを使用します。

そこでcookies.signedメソッドを使用する箇所があります。

このメソッドが使用できません。

これはテスト環境でcookiesのクラスが異なるためです。

参考になりました↓

Railsのインテグレーションテストでcookies.signedを使いたい

よって、新たにRSpecのヘルパーを作りcookiesに干渉しないログイン確認用メソッドis_logged_in?を定義します。


RSpecのヘルパーとメソッドをつくる

RSpecのヘルパーをつくるにはspec/support配下にファイルを作成する必要があります。

$ mkdir spec/support

$ touch spec/support/application_helper.rb

でそのファイルをこうする。


spec/support/application_helper.rb

module ApplicationHelpers

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


あとは今作ったApplicationHelpersモジュール/ヘルパーをRSpecに認識してもらう。


spec/rails_helper.rb

# コメントアウトを外す↓(まだやっていない方)

Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# 中略
RSpec.configure do |config|
# 追加↓
config.include ApplicationHelpers

これにて準備完了。


Request specを再編集

あとはテスト内のlogged_in?をis_logged_in?にするだけ。


spec/requests/users_logins_spec.rb

require 'rails_helper'

# 中略
expect(is_logged_in?).to be_falsey # 2つあるので両方変更
# 中略

これでテストグリーン。

参考にさせていただきました↓

RSpecでヘルパーを作成する方法



発展:このテストを無理やりSystem specで書いてしまったら

(本来はRequest specで書きます)


/spec/systems/signup_spec.rb

require 'rails_helper'

RSpec.describe "Signups", type: :system do
include SessionsHelper
# 中略
it "succeeds to create account then login automatically" do
visit signup_path
expect {
fill_in '名前', with: 'Example User'
fill_in 'メールアドレス', with: 'user@example.com'
fill_in 'パスワード', with: 'password'
fill_in 'パスワード(再入力)', with: 'password'
find(".form-submit").click
}.to change(User, :count).by(1)
expect(is_logged_in?).to be true
expect(current_path).to eq user_path(1)
expect(page).not_to have_selector '#error_explanation'
end
# 中略


これに注目。

expect(is_logged_in?).to be true

これでもエラーになる。

(エラー画面の保存忘れました...)

System specではsessionが効かないらしい!

session変数を操作したい場合Request specで書くのがベストのようです。

ここでSystem specを再認識しよう。

下記は「【動画付き】Railsチュートリアルの統合テスト(integration test)は、RSpecのリクエストスペックに置き換えるのがラクです」より引用。


基本的な考え方としては、「システムスペックはブラウザ上でできることにフォーカスしてテストを書く(やむを得ない場合のみ、サーバーサイドの処理やデータを覗き見る)」


じゃあどうするのかというと、

「ブラウザ上で検証できる箇所で確認する」

というのがSystem specでの解法です。

だからさっきのpassword_digestもSystem specでは馴染まないのね。

ここまでの知識を助けてくれた記事↓

undefined method `session' for nil:NilClass in RSpec Rails

RSpecでテストを書いていたら別ファイルのhelperメソッドの呼び出しが undefined method になって困った時

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

【動画付き】Railsチュートリアルの統合テスト(integration test)は、RSpecのリクエストスペックに置き換えるのがラクです



レイアウトは変更されたか

続いてSystem specでのテスト。

軽微な変更としてclick_onを使用せずfind().clickを使用します。

(ログイン画面に同名のボタンが複数存在するため)


spec/systems/login_spec.rb

require 'rails_helper'

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

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

context "invalid" do
it "is invalid because it has no information" do
visit login_path
expect(page).to have_selector '.login-container'
fill_in 'メールアドレス', with: ''
fill_in 'パスワード', with: ''
find(".form-submit").click
expect(current_path).to eq login_path
expect(page).to have_selector '.login-container'
expect(page).to have_selector '.alert-danger'
end

it "deletes flash messages when users input invalid information then other links" do
visit login_path
expect(page).to have_selector '.login-container'
fill_in 'メールアドレス', with: ''
fill_in 'パスワード', with: ''
find(".form-submit").click
expect(current_path).to eq login_path
expect(page).to have_selector '.login-container'
expect(page).to have_selector '.alert-danger'
visit root_path
expect(page).not_to have_selector '.alert-danger'
end
end

context "valid" do
it "is valid because it has valid information" do
visit login_path
fill_in 'メールアドレス', with: user.email
fill_in 'パスワード', with: 'password'
find(".form-submit").click
expect(current_path).to eq user_path(1)
expect(page).to have_selector '.show-container'
end

it "contains logout button without login button" do
visit login_path
fill_in 'メールアドレス', with: user.email
fill_in 'パスワード', with: 'password'
find(".form-submit").click
expect(current_path).to eq user_path(1)
expect(page).to have_selector '.show-container'
expect(page).to have_selector '.btn-logout-extend'
expect(page).not_to have_selector '.btn-login-extend'
end
end
end



ERbが使えないのをどう考えるか

Tutorialのテストをそのまま実装しようとすると仮のユーザが必要でした。

TutorialではこのようにしてFixturesにpassword_digestを表現しました。


test/fixtures/users.yml

michael:

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

RSpecではどうするのかというと、、

先に答えを言うとpassword_digestはなくても大丈夫っぽいです。

(誤りの可能性もございます)

ここからは試行錯誤した上での失敗です。

スラスラ読みたい方はサインアップ時にログインを済ませるまで飛ばしましょう

(必要ない箇所を分かりやすくするため、始終に二重ラインを引きます)



さて困ったのはFactoryBotではERbが使えません!!

というわけで、色々考えてみよう。


FactoryBot内にUser.digestを定義

例えばこんなんどうでしょう。


spec/factories/users.rb

FactoryBot.define do

factory :user do
name "Michael Example"
email "michael@example.com"
password "password"
password_confirmation "password"
password_digest User.digest("password")
end

def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
end



bash

undefined method 'name' in 'user' factory


単純にエラーが発生しました。

どうやらFactoryBotのキーに対する値には{}が必要のようです↓

Factory_Botで undefined method 'name' in 'user' factoryが発生する場合

変更。


spec/factories/users.rb

FactoryBot.define do

factory :user do
name { "Michael Example" }
email { "michael@example.com" }
password { "password" }
password_confirmation { "password" }
password_digest { User.digest("password") }
end

def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
end


遅いのでSystem specのみテスト。


bash

$ rails spec:systems

11 examples, 0 failures


実はこれでテストが通ってしまう。


本当にUser.digestを介しているのか問題。

ん?でもこれ本当に正しいのか?

怖くなったのでFactoryBotに書かれたUser.digestをコメントアウト。


spec/factories/users.rb

  # 中略

# def User.digest(string)
# cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
# BCrypt::Engine.cost
# BCrypt::Password.create(string, cost: cost)
# end
end


bash

$ rails spec:systems

11 examples, 0 failures


あ、これはだめだ:scream:


可能性1:パスワードなしでも通ってしまう? → ×

テストでパスワード入力しなくても通るのでは?


spec/systems/login_spec.rb

# 中略

it "is valid with because it has valid information" do
visit login_path
fill_in 'メールアドレス', with: user.email
# 変更
fill_in 'パスワード', with: ''
find(".form-submit").click
expect(current_path).to eq user_path(1)
expect(page).to have_selector '.show-container'
end
# 中略


bash

$ rails spec:systems

11 examples, 1 failures


そうではないらしい。入力しないと通らないってのは機能してる。


可能性2:違うパスワードでも通ってしまう? → ×

テストで違うパスワードでも通るのでは?


spec/systems/login_spec.rb

# 中略

it "is valid with because it has valid information" do
visit login_path
fill_in 'メールアドレス', with: user.email
# 変更
fill_in 'パスワード', with: 'foobar'
find(".form-submit").click
expect(current_path).to eq user_path(1)
expect(page).to have_selector '.show-container'
end
# 中略


bash

$ rails spec:systems

11 examples, 1 failures


これも違う。間違ったパスワードだと通らない。


可能性3:FactoryBot内のpassword_digestの宣言が効いていない → ○

テストは元どおりにして、こんなことしてみた。


spec/factories/users.rb

FactoryBot.define do

factory :user do
name { "Michael Example" }
email { "michael@example.com" }
password { "password" }
password_confirmation { "password" }
# password_digest { User.digest("password") }
end

# def User.digest(string)
# cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
# BCrypt::Engine.cost
# BCrypt::Password.create(string, cost: cost)
# end
end



bash

$ rails spec:systems

11 examples, 0 failures


おーまじか。password_digest宣言せずともログインできるのね??


推論:password_digestは勝手に行ってくれる?

考えるとそうっぽいです。

そもそもSystem specはアプリケーション内を操作した上での検証なので、password_digestを直接入力するといった行為は馴染まないのかもしれません。

だからFactoryBotでpassword_digestを宣言しなくとも裏でやってくれるのかも。

※ このもやもやはさっき述べたSystem specの特性によるもの

アプリで求めていた動作は出来ていたので、

可能性3でコメントアウトした箇所を削除し、次に進むことにします。


spec/factories/users.rb

# 可能性3をご確認ください





サインアップ時にログインを済ませる

あとは簡単です。さっき作ったloginメソッドを使うだけ。


app/controllers/users_controller.rb

# 中略

def create
@user = User.new(user_params)
if @user.save
log_in @user
flash[:success] = "Welcome to the Sample App!"
redirect_to @user
else
render 'new'
end
end
# 中略

テストも追加。


spec/requests/users_signups_spec.rb

require 'rails_helper'

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

describe "GET /signup" do
it "is invalid signup information" do
get signup_path
expect {
post signup_path, params: {
user: {
name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar"
}
}
}.not_to change(User, :count)
expect(is_logged_in?).to be_falsey
end

it "is valid signup information" do
get signup_path
expect {
post signup_path, params: {
user: {
name: "Example User",
email: "user@example.com",
password: "password",
password_confirmation: "password"
}
}
}.to change(User, :count).by(1)
expect(is_logged_in?).to be_truthy
end
end
end


これでokkk。


機能としてのログアウトを実装する

残りはログアウト。Tutorial通りに実装しよう。

まずSessionsヘルパーにlog_outメソッドを定義。


/app/helpers/sessions_helper.rb

# 中略

def log_out
session.delete(:user_id)
@current_user = nil
end

次にSessionsコントローラ。


app/controllers/sessions_controller.rb

# 中略

def destroy
log_out
redirect_to root_url
end


ログアウトのテストを実装する

残るはテスト。

編集も加えたので全て載せる。


spec/requests/users_logins_spec.rb

require 'rails_helper'

RSpec.describe "UsersLogins", type: :request do
include SessionsHelper

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

describe "GET /login" do
context "invalid information" do
it "fails having a danger flash message" do
get login_path
post login_path, params: {
session: {
email: "",
password: ""
}
}
expect(flash[:danger]).to be_truthy
expect(is_logged_in?).to be_falsey
end
end

context "valid information" do
it "succeeds having no danger flash message" do
get login_path
post login_path, params: {
session: {
email: user.email,
password: user.password
}
}
expect(flash[:danger]).to be_falsey
expect(is_logged_in?).to be_truthy
end

it "succeeds login and logout" do
get login_path
post login_path, params: {
session: {
email: user.email,
password: user.password
}
}
expect(is_logged_in?).to be_truthy
delete logout_path
expect(is_logged_in?).to be_falsey
end
end
end
end



/spec/systems/login_spec.rb

require 'rails_helper'

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

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

describe "Login" do
context "invalid" do
it "is invalid because it has no information" do
visit login_path
expect(page).to have_selector '.login-container'
fill_in 'メールアドレス', with: ''
fill_in 'パスワード', with: ''
find(".form-submit").click
expect(current_path).to eq login_path
expect(page).to have_selector '.login-container'
expect(page).to have_selector '.alert-danger'
end

it "deletes flash messages when users input invalid information then other links" do
visit login_path
expect(page).to have_selector '.login-container'
fill_in 'メールアドレス', with: ''
fill_in 'パスワード', with: ''
find(".form-submit").click
expect(current_path).to eq login_path
expect(page).to have_selector '.login-container'
expect(page).to have_selector '.alert-danger'
visit root_path
expect(page).not_to have_selector '.alert-danger'
end
end

context "valid" do
it "is valid because it has valid information" do
visit login_path
fill_in 'メールアドレス', with: user.email
fill_in 'パスワード', with: 'password'
find(".form-submit").click
expect(current_path).to eq user_path(1)
expect(page).to have_selector '.show-container'
end

it "contains logout button without login button" do
visit login_path
fill_in 'メールアドレス', with: user.email
fill_in 'パスワード', with: 'password'
find(".form-submit").click
expect(current_path).to eq user_path(1)
expect(page).to have_selector '.show-container'
expect(page).to have_selector '.btn-logout-extend'
expect(page).not_to have_selector '.btn-login-extend'
end
end
end

# ここがログアウトのテスト
describe "Logout" do
it "contains login button without logout button" do
visit login_path
fill_in 'メールアドレス', with: user.email
fill_in 'パスワード', with: 'password'
find(".form-submit").click
expect(current_path).to eq user_path(1)
expect(page).to have_selector '.show-container'
expect(page).to have_selector '.btn-logout-extend'
expect(page).not_to have_selector '.btn-login-extend'
click_on 'ログアウト'
expect(current_path).to eq root_path
expect(page).to have_selector '.home-container'
expect(page).to have_selector '.btn-login-extend'
expect(page).not_to have_selector '.btn-logout-extend'
end
end
end


注意:

ファイル名にloginとありますが、関係が深いのでログアウトもこのテストに入れています。

ふぉわたぁぁ!!!


追記

[2019年8月17日]

記事改変しました。

前回:#7 ログイン下準備編

次回:#9 永続セッション, cookie編