Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions spec/factories/users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FactoryBot.define do
factory :user do
email { Faker::Internet.unique.email }
password { 'password' }
password_confirmation { 'password' }
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
end
end
1 change: 1 addition & 0 deletions spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,5 @@
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
config.include FactoryBot::Syntax::Methods
end
9 changes: 7 additions & 2 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@

# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
=begin

# This allows you to limit a spec run to individual examples or groups
# you care about by tagging them with `:focus` metadata. When nothing
# is tagged with `:focus`, all examples get run. RSpec also provides
# aliases for `it`, `describe`, and `context` that include `:focus`
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
config.filter_run_when_matching :focus

=begin
# Allows RSpec to persist some state between runs in order to support
# the `--only-failures` and `--next-failure` CLI options. We recommend
# you configure your source control system to ignore this file.
Expand Down Expand Up @@ -93,4 +93,9 @@
# as the one that triggered the failure.
Kernel.srand config.seed
=end

config.before(:each, type: :system) do
driven_by :selenium, using: :headless_chrome, screen_size: [1920, 1080]
# driven_by :selenium, using: :chrome, screen_size: [1080, 1080] # ローカルでの確認用
end
end
92 changes: 92 additions & 0 deletions spec/system/posts_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
require 'rails_helper'

RSpec.describe "Posts", type: :system do
describe '確認観点4:投稿機能' do
let!(:user){ create(:user) }
let(:another_user){ create(:user) }

before do
# 確認対象の画面に移動
visit '/login'

# labelの存在確認
expect(page).to have_selector('label',text: 'Email'), 'Email というラベルが表示されていることを確認してください'
expect(page).to have_selector('label', text: 'Password'), 'Password というラベルが表示されていることを確認してください'

# labelとフィールドの対応付け確認
expect(page).to have_css("label[for='email']"), 'Email というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'
expect(page).to have_css("label[for='password']"), 'Password というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'

# ログイン用ボタンの存在確認
expect(page).to have_button('Login'), 'ログイン用のボタンが表示されていることを確認してください'

# ユーザーログイン処理
fill_in 'Email', with: user.email
fill_in 'Password', with: 'password'
click_button 'Login'
end

it '4-1:自分の投稿を編集することができる' do
create(:post, user: user)

# 確認対象の画面に移動
visit '/posts'

# 編集リンクの存在確認
expect(page).to have_content('Edit'), '自分の投稿に編集用のリンクが表示されているかを確認してください'
click_on 'Edit'

# labelの存在確認
expect(page).to have_selector('label', text: 'Title'), 'Title というラベルが表示されていることを確認してください'
expect(page).to have_selector('label', text: 'Content'), 'Content というラベルが表示されていることを確認してください'

# labelとフィールドの対応付け確認
expect(page).to have_css("label[for='post_title']"), 'Title というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'
expect(page).to have_css("label[for='post_content']"), 'Content というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'

# 更新ボタンの存在確認
expect(page).to have_button('Update Post'), '編集画面に更新用のボタンが表示されているかを確認してください'

# 投稿編集と更新処理
fill_in 'Title', with: 'edited_title'
fill_in 'Content', with: 'edited_content'
click_on 'Update Post'

# 処理結果の確認
expect(current_path).to eq(post_path(Post.find_by(title: 'edited_title'))), '投稿編集後に投稿詳細画面に遷移できていません'
expect(page).to have_content('edited_title'), '編集した投稿のタイトルが表示されていません'
expect(page).to have_content('edited_content'), '編集した投稿の本文が表示されていません'
end

it '4-2:自分の投稿を削除することができる' do
post = create(:post, user: user)

# 確認対象の画面に移動
visit '/posts'

# 削除リンクの存在確認
expect(page).to have_content('Destroy'), '自分の投稿に削除用のリンクが表示されているかを確認してください'
page.accept_confirm { click_on 'Destroy' }

# 処理結果の確認
expect(current_path).to eq('/posts'), '投稿削除後に投稿一覧画面に遷移できていません'
expect(page).not_to have_content(post.title), '投稿が削除できているかを確認してください'
end

it '4-3:他人の投稿に編集リンクが表示されない' do
create(:post, user: another_user)

# 確認対象の画面に移動
visit '/posts'
expect(page).not_to have_link('Edit'), '他人の投稿に編集リンクが表示されています'
end

it '4-4:他人の投稿に削除リンクが表示されない' do
create(:post, user: another_user)

# 確認対象の画面に移動
visit '/posts'
expect(page).not_to have_link('Destroy'), '他人の投稿に削除リンクが表示されています'
end
end
end
197 changes: 197 additions & 0 deletions spec/system/user_sessions_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@

require 'rails_helper'

RSpec.describe "UserSessions", type: :system do
describe '確認観点2:ユーザーログイン' do
it '2-1:ユーザーのログインができる' do
# テストデータの用意
user = create(:user) # describe使わないので、let!を使わずに記載

# 確認対象の画面に移動
visit '/login'

# labelの存在確認
expect(page).to have_selector('label', text: 'Email'), 'Email というラベルが表示されていることを確認してください'
expect(page).to have_selector('label', text: 'Password'), 'Password というラベルが表示されていることを確認してください'

# labelとフィールドの対応付け確認
expect(page).to have_css("label[for='email']"), 'Email というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'
expect(page).to have_css("label[for='password']"), 'Password というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'
Comment on lines +18 to +19

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここむずいっすねー。
form_with scope: :session とかで書かれるとテストが通らなくなる。そしてscope: :sessionって書くのは間違いではない。

@yuji91 yuji91 Apr 19, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

最終的には/loginuser_sessions#newに遷移するようにパスを指定してlogin_pathで視認性を上げて開発するのが理想かと思うので、login_pathを使うことを要件として要求しても良いのかな、と思いました。

そうすれば<%= form_with url: login_pathと書く部分を<%= form_with scope: :sessionと記載されてテストが通らない事例が発生しても、
質問とかで「scopeで記載しても動作は問題ないですが、今回は要件に指定があるので慣例通りlogin_pathを使って実装してみましょう!」みたいな対応記録を残しておけば良いのかな、と思いました!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

いや、scope: :sessionを書くと

name=session[email]

のようになるので

expect(page).to have_css("label[for='email']"), 

が通らなくなるよって話です!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scope: :sessionを記載した形だと以下の2点が発生する認識ですかね、

  • Controller側ではparams[:session][:email]などの形でパラメータを受け取る必要がある
    Image from Gyazo

  • labelについてsession_emailの様な値になり、テストコードが通らない
    Image from Gyazo

上記の形が望ましいとも思えないので、課題要件でscope: :sessionと書くと満たせない様な要件を設定してしまうのはどうでしょうか?
(「ログイン画面のform_withではurl: login_pathの形で引数を指定してください」など)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

「ログイン画面のform_withではurl: login_pathの形で引数を指定してください」など

urlオプションとscopeオプションは共存できるのでその案内はちょい違和感あるかな〜。

当初伊藤くんが提案してくれた通り

そうすれば<%= form_with url: login_pathと書く部分を<%= form_with scope: :sessionと記載されてテストが通らない事例が発生しても、
質問とかで「scopeで記載しても動作は問題ないですが、今回は要件に指定があるので慣例通りlogin_pathを使って実装してみましょう!」みたいな対応記録を残しておけば良いのかな、と思いました!

↑ でいきますかー。

ちなみに個人的にはsessionというキーでグルーピングするほうがむしろ望ましいかなぁとは思ってて、基礎編の実装ミスったなぁと感じてる笑

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

おけです、では以下の対応で良いですかね?

  • ログイン用のパスはlogin_pathを使って実装する指示を課題要件に記載する
  • scope: :sessionで実装した形でテストが通らない場合は、質問してもらってそこで対応して記録を残す

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

おkっす〜

@yuji91 yuji91 Apr 22, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

改めて考えたのですが、scopeはリクエストパラメータに渡す情報をparams[:sessions]の様に階層で分けたい場合に使うことがある、ということですよね。
https://apidock.com/rails/ActionView/Helpers/FormHelper/form_with

そこでscopeを設定されてしまうとlabel[for='email']の値がずれてしまう。

これまでの流れを整理しました。

  • 「labelとフィールドの対応付け確認」をテストコードから省略しない場合は、scopeを設定されると自動テストが通らず、生徒のローカルでは問題なくログインできるという現象が発生する。

  • 「labelとフィールドの対応付け確認」をテストコードから省略すれば、生徒側でscopeを自由に設定しても自動テストは成功する。

観点:「labelとフィールドの対応付け確認」はテストコードから省略すると問題になるか?
--> 省略した場合、課題要件で「ラベルをクリックして対応する入力フォームがアクティブになることを確認してください」という形になる。見逃されたら「ローカルでログインできるのにテストが通らない」とか言われるので、ここは自動テストで指摘できる形で残しておいた方が良さそう。

--> 残すのであれば「scopeを設定すると自動テストが失敗する」ことをどこかで伝えなくてはならない
--> 課題要件ではなく、質問で投稿してもらってそれを他の生徒が見られるように記録する。(今までの話の流れ)

--> 最初から課題要件で「自動テストの都合上、ログインフォームのform_withのscopeは設定しないで実装してください」と指示しておいた方が親切?

--> 質問として記録するのは良いとして、あらかじめ課題要件で記載してあげた方が良い気がしました!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

めちゃわかりやすいまとめ笑

--> 質問として記録するのは良いとして、あらかじめ課題要件で記載してあげた方が良い気がしました!

そうすね〜。それでいきましょか!


# ログイン用ボタンの存在確認
expect(page).to have_button('Login'), 'ログイン用のボタンが表示されていることを確認してください'

# ユーザーログイン処理
fill_in 'Email', with: user.email
fill_in 'Password', with: 'password'
click_button 'Login'

# 処理結果の確認
expect(current_path).not_to eq('/login'), 'ログイン処理が正しく行えるかを確認してください'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一行下のexpectがあればこれは不要?

@yuji91 yuji91 Apr 23, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ちょっと迷ったんですが、他の観点でも

  1. ログインが正しくできるか or 正しく失敗するか
    -->「 ログイン成功の場合/loginから変わったか」「ログイン失敗の場合/postsに移動していないか」で判断

  2. 「ログイン成功時に/postsに正しく画面遷移出来ているか」「ログイン失敗時に/loginから遷移されていないか」を確認

と言う段階を分けて確認するつもりで書いていました。
これはまとめちゃっても良いんじゃないか、と言うことですかね?
Image from Gyazo

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほどー。
じゃぁ今のままで良いか!

expect(current_path).to eq('/posts'), 'ログイン後に投稿一覧画面に遷移できていません'
end

it '2-2:入力項目が不足している場合にログインができない' do
# テストデータの用意
user = create(:user) # describe使わないので、let!を使わずに記載

# 確認対象の画面に移動
visit '/login'

# labelの存在確認
expect(page).to have_selector('label', text: 'Email'), 'Email というラベルが表示されていることを確認してください'
expect(page).to have_selector('label', text: 'Password'), 'Password というラベルが表示されていることを確認してください'

# labelとフィールドの対応付け確認
expect(page).to have_css("label[for='email']"), 'Email というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'
expect(page).to have_css("label[for='password']"), 'Password というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'

# ログイン用ボタンの存在確認
expect(page).to have_button('Login'), 'ログイン用のボタンが表示されていることを確認してください'

# ユーザーログイン処理
fill_in 'Email', with: nil
fill_in 'Password', with: nil
click_button 'Login'

# 処理結果の確認
expect(current_path).not_to eq('/posts'), '入力項目が不足している場合にログインできていないかを確認してください'
expect(current_path).to eq('/login'), 'ログインの失敗時に別の画面の遷移していないかを確認してください'
end

it '2-3:存在しないユーザーでログインができない' do
# テストデータの用意
user = create(:user) # describe使わないので、let!を使わずに記載

# 確認対象の画面に移動
visit '/login'

# labelの存在確認
expect(page).to have_selector('label', text: 'Email'), 'Email というラベルが表示されていることを確認してください'
expect(page).to have_selector('label', text: 'Password'), 'Password というラベルが表示されていることを確認してください'

# labelとフィールドの対応付け確認
expect(page).to have_css("label[for='email']"), 'Email というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'
expect(page).to have_css("label[for='password']"), 'Password というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'

# ログイン用ボタンの存在確認
expect(page).to have_button('Login'), 'ログイン用のボタンが表示されていることを確認してください'

# ユーザーログイン処理
fill_in 'Email', with: 'another_user@example.com'
fill_in 'Password', with: 'password'
click_button 'Login'

# 処理結果の確認
expect(current_path).not_to eq('/posts'), '存在しないユーザーでログインできていないかを確認してください'
expect(current_path).to eq('/login'), 'ログインの失敗時に別の画面の遷移していないかを確認してください'
end

it '2-4:パスワードが間違っている場合にログインができない' do
# テストデータの用意
user = create(:user) # describe使わないので、let!を使わずに記載

# 確認対象の画面に移動
visit '/login'

# labelの存在確認
expect(page).to have_selector('label', text: 'Email'), 'Email というラベルが表示されていることを確認してください'
expect(page).to have_selector('label', text: 'Password'), 'Password というラベルが表示されていることを確認してください'

# labelとフィールドの対応付け確認
expect(page).to have_css("label[for='email']"), 'Email というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'
expect(page).to have_css("label[for='password']"), 'Password というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'

# ログイン用ボタンの存在確認
expect(page).to have_button('Login'), 'ログイン用のボタンが表示されていることを確認してください'

# ユーザーログイン処理
fill_in 'Email', with: user.email
fill_in 'Password', with: 'wrong_password'
click_button 'Login'

# 処理結果の確認
expect(current_path).not_to eq('/posts'), 'パスワードが間違っている場合にログインできていないかを確認してください'
expect(current_path).to eq('/login'), 'ログインの失敗時に別の画面の遷移していないかを確認してください'
end

it '2-5:複数ユーザー登録時に問題なくログインができる' do
# テストデータの用意
first_user = create(:user) # describe使わないので、let!を使わずに記載
second_user = create(:user) # describe使わないので、let!を使わずに記載
third_user = create(:user) # describe使わないので、let!を使わずに記載

# 確認対象の画面に移動
visit '/login'

# labelの存在確認
expect(page).to have_selector('label', text: 'Email'), 'Email というラベルが表示されていることを確認してください'
expect(page).to have_selector('label', text: 'Password'), 'Password というラベルが表示されていることを確認してください'

# labelとフィールドの対応付け確認
expect(page).to have_css("label[for='email']"), 'Email というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'
expect(page).to have_css("label[for='password']"), 'Password というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'

# ログイン用ボタンの存在確認
expect(page).to have_button('Login'), 'ログイン用のボタンが表示されていることを確認してください'

# ユーザーログイン処理
fill_in 'Email', with: second_user.email
fill_in 'Password', with: 'password'
click_button 'Login'

# 処理結果の確認
expect(current_path).not_to eq('/login'), 'ログイン処理が正しく行えるかを確認してください'

find('#header-profile').click
expect(page).not_to have_content("#{first_user.last_name} #{first_user.first_name}"), '最初に登録されたユーザーでログインされています。ログイン時のロジックを確認してください'
expect(page).to have_content("#{second_user.last_name} #{second_user.first_name}"), '複数のユーザーを作成した状態で、正しくログインができるかを確認してください'
expect(page).not_to have_content("#{third_user.last_name} #{third_user.first_name}"), '最後に登録されたユーザーでログインされています。ログイン時のロジックを確認してください'
end
end

describe '確認観点3:ユーザーログアウト' do
it '3-1:ユーザーのログアウトができる' do
# テストデータの用意
user = create(:user) # describe使わないので、let!を使わずに記載

# 確認対象の画面に移動
visit '/login'

# labelの存在確認
expect(page).to have_selector('label', text: 'Email'), 'Email というラベルが表示されていることを確認してください'
expect(page).to have_selector('label', text: 'Password'), 'Password というラベルが表示されていることを確認してください'

# labelとフィールドの対応付け確認
expect(page).to have_css("label[for='email']"), 'Email というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'
expect(page).to have_css("label[for='password']"), 'Password というラベルをクリックすると対応するフィールドにフォーカスすることを確認してください'

# ログイン用ボタンの存在確認
expect(page).to have_button('Login'), 'ログイン用のボタンが表示されていることを確認してください'

# ユーザーログイン処理
fill_in 'Email', with: user.email
fill_in 'Password', with: 'password'
click_button 'Login'

# ユーザーログアウト処理
find('#header-profile').click

# ログアウト用ボタンの存在確認
expect(page).to have_link('Logout'), 'ログアウトのボタンが表示されていることを確認してください'

click_on 'Logout'

# 処理結果の確認
expect(page).to have_button('Login'), 'ログアウトができているかを確認してください'
end

it '3-2:ログインしていない場合、ユーザーのログアウトリンクが表示されない' do
# 確認対象の画面に移動
visit '/login'

# 処理結果の確認
expect(page).not_to have_link('Logout'), 'ログインしていない場合でも、ログアウトリンクが表示されています'
end
end
end
Loading