テスト対象
該当するコントローラ | 該当するアクション |
---|---|
UsersController | show |
環境
フォルダ、使用ファイル
種類 | ファイル名 |
---|---|
スペック | spec/features/users_profile_spec.rb |
スペック | spec/features/authorization_spec.rb |
サポートモジュール | spec/support/support_module.rb |
shared_context | spec/support/shared_context.rb |
shared_examples | spec/support/shared_examples.rb |
ファクトリ(ユーザ) | spec/support/factories/users.rb |
ファクトリ(マイクロポスト) | spec/support/factories/microposts.rb |
アウトラインの作成 1/4
-
ページタイトル、パス、リンク
-
※ 未ログインの場合(ページ保護)のテストは
authorization_spec.rb
(別ファイル)にて作成
フィーチャスペック編 authorization_spec
users_profile_spec.rb
spec/features/users_profile_spec.rb
RSpec.feature "UsersProfile", type: :feature do
describe "profile"
# ページタイトルと見出しが正しいこと
scenario "have title/heading"
# パスが正しいこと
scenario "have correct path"
# 存在すべきリンクが表示されること
scenario "have links of profile-page"
end
スペック作成 1/4
↓の部分は、shared_examples(別ファイル)に作成
scenario "have links of profile-page"
users_profile_spec.rb
spec/features/users_profile_spec.rb
include SupportModule
include_context "setup"
subject { page }
describe "profile" do
before { login_as(user) }
# => SupportModule, shared_context 内で定義
scenario "have title/heading" do
should have_title(user.name)
should have_css("h1", text: user.name)
end
# パスが正しいこと
scenario "have correct path" do
should have_current_path(user_path(user)) # or
expect(current_path).to eq user_path(user)
end
# 存在すべきリンクが表示されること
it_behaves_like "have links of profile-page"
end
end
SupportModule, shared_context
-
before { login_as(user) }
の部分shared_context.rb# spec/support/shared_context.rb RSpec.shared_context "setup" do # 遅延評価、呼ばれた時にDB保存される let(:user) { create(:user) } end
support_module.rb# spec/support/support_module.rb module SupportModule def login_as(user) visit root_path click_link "Log in" fill_in "Email", with: user.email fill_in "Password", with: user.password click_button "Log in" end end
shared_examples の作成
-
it_behaves_like "have links of profile-page"
の部分shared_examples.rb# spec/support/shared_examples.rb # links # users#show shared_examples_for "have links of profile-page" do it { expect(page).to have_link("Users", href: "/users") } it { expect(page).to have_link("Profile", href: user_path(user)) } it { expect(page).to have_link("Settings", href: edit_user_path(user)) } it { expect(page).to have_link("Log out", href: "/logout") } it { expect(page).not_to have_link("Log in", href: "/login") } end
実行結果 1/4
$ bin/rspec spec/features/users_profile_spec.rb -e "profile"
UsersProfile
profile
have title/heading
have correct path
behaves like have links of profile-page
should have visible link "Users" with href "/users"
should have visible link "Profile" with href "/users/5"
should have visible link "Settings" with href "/users/5/edit"
should have visible link "Log out" with href "/logout"
should not have visible link "Log in" with href "/login"
Finished in 5.45 seconds (files took 2.17 seconds to load)
7 examples, 0 failures
アウトラインの作成 2/4
users_profile_spec.rb
# spec/features/users_profile_spec.rb
RSpec.feature "UsersProfile", type: :feature do
describe "profile"
# (省略)
# ユーザー情報
describe "user-info"
# ユーザー情報が表示されていること
scenario "have user profile infomation"
# マイクロポストの統計
describe "micropost stats"
# フォロー数/フォロワー数のリンクと数字が表示されていること
scenario "have link user's following/followers"
end
スペック作成 2/4
↓の部分は shared_examples(別ファイル)に作成
scenario "have user profile infomation"
scenario "have link user's following/followers"
users_profile_spec.rb
# spec/features/users_profile_spec.rb
require 'rails_helper'
RSpec.feature "UsersProfile", type: :feature do
include SupportModule
include_context "setup"
subject { page }
describe "profile" do
before { login_as(user) } # => SupportModule
# (省略)
# ユーザー情報
describe "user-info" do
before { my_posts }
# (セットアップの確認)
it { expect(user.microposts.count).to eq my_posts.count }
it_behaves_like "have user profile infomation"
end
# マイクロポストの統計
describe "micropost stats" do
before do
my_posts
other_user
other_posts
user.follow(other_user)
other_user.follow(user)
end
# (セットアップの確認)
it { expect(user.microposts.count).to eq my_posts.count }
it { expect(other_user.microposts.count).to eq other_posts.count }
it_behaves_like "have link user's following/followers"
end
end
end
shared_context の作成
-
ユーザ、マイクロポストのセットアップの定義
shared_context.rbspec/support/shared_context.rb RSpec.shared_context "setup" do # ユーザ # 遅延評価、呼ばれた時にDB保存される let(:user) { create(:user) } let(:other_user) { create(:other_user) } # マイクロポスト # 自分の投稿 let(:my_posts) { create_list(:user_post, 30, user: user) } # 他人の投稿 let(:other_posts) { create_list(:other_user_post, 30, user: other_user) } end
サンプルデータ has_many な関係を定義する
let(:my_posts) { create_list(:user_post, 30) }
ではなく、
let(:my_posts) { create_list(:user_post, 30, user: user) }
とする
-
同じファクトリ(user)を作成しようとして、email 重複エラーにならないように、インスタンス変数(user)を明示的に渡して1対多になるようにする
(抜粋)email 重複エラーになる $ rails console test --sandbox [2] pry(main)> user = FactoryBot.create(:user) id: 5, name: "Example user", email: "user@example.com", created_at: Fri, 03 Aug 2018 02:10:18 UTC +00:00, updated_at: Fri, 03 Aug 2018 02:10:18 UTC +00:00, password_digest: "$2a$04$TB5uZ0B1Zti3Le1a3YV7EOcE72HxAT/GfLoiuU2s23b4IBfFjeiNK", remember_digest: nil, admin: false, activation_digest: "$2a$04$fkkSn6p5ZqaUGXNc0PAUB.HXf4LchtR35S0Fs6C4Bk0ujw5Sg1igq", activated: false, activated_at: nil> [6] pry(main)> mypost = FactoryBot.create_list(:user_post, 1) (0.2ms) SAVEPOINT active_record_1 User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "user@example.com"], ["LIMIT", 1]] (0.1ms) ROLLBACK TO SAVEPOINT active_record_1 ActiveRecord::RecordInvalid: Validation failed: Email has already been taken from /home/vagrant/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.1.4/lib/active_record/validations.rb:78:in `raise_validation_error'
ファクトリの作成
-
ユーザ/マイクロポスト
users.rb# spec/factories/users.rb FactoryBot.define do # 自分 factory :user, class: User do name "Example user" email "user@example.com" password "foobar" password_confirmation "foobar" admin false # 他人 factory :other_user do name { Faker::Name.name } email { Faker::Internet.email } end end end
microposts.rb# spec/factories/micoroposts.rb FactoryBot.define do # 自分のマイクロポスト factory :user_post, class: Micropost do content { Faker::Lorem.sentence(5) } # association :[関連するモデル名], factory: :[任意につけたファクトリ名] association :user, factory: :user # 他人のマイクロポスト factory :other_user_post do content { Faker::Lorem.sentence(5) } association :user, factory: :other_user end end end
shared_examples の作成
-
it_behaves_like "have user profile infomation"
-
it_behaves_like "have link user's following/followers"
shared_examples.rb# spec/support/shared_examples.rb # ユーザー情報 # top#home, users#following, users#followers shared_examples_for "have user infomation" do it { expect(page).to have_css('img.gravatar') } it { expect(page).to have_css('h1', text: user.name) } it { expect(page).to have_text("Microposts") } it { expect(page).to have_link("view my profile", href: user_path(user)) } end # マイクロポストの統計情報 # tops#home, users#show shared_examples_for "have link user's following/followers" do it { expect(page).to have_link("following", href: following_user_path(user)) } it { expect(page).to have_link("followers", href: followers_user_path(user)) } end
実行結果 2/4
$ bin/rspec spec/features/users_profile_spec.rb -e "micropost stats"
UsersProfile
profile
micropost stats
should eq 30
should eq 30
behaves like have link user's following/followers
should have visible link "following" with href "/users/5/following"
should have visible link "followers" with href "/users/5/followers"
Finished in 6.51 seconds (files took 2.48 seconds to load)
4 examples, 0 failures
アウトラインの作成 3/4
users_profile_spec.rb
spec/features/users_profile_spec.rb
RSpec.feature "UsersProfile", type: :feature do
describe "profile"
# (省略)
# マイクロポスト
describe "microposts"
# マイクロポストが表示されていること
scenario "pagination list each micropost"
end
スペック作成 3/4
users_profile_spec.rb
# spec/features/users_profile_spec.rb
RSpec.feature "UsersProfile", type: :feature do
describe "profile"
before { login_as(user) }
# (省略)
# マイクロポスト
describe "microposts" do
before { my_posts }
# (セットアップの確認)
it { expect(user.microposts.count).to eq my_posts.count }
# マイクロポストが表示されていること
scenario "pagination list each micropost" do
# ???NGになってしまう
# should have_css("h3", text: "Micropost (#{user.microposts.count})")
expect {
user.microposts.paginate(page: 1).each do |post|
should have_link("img.gravatar", href: post.user)
should have_link("#{micropost.user.name}", href: post.user)
should have_css("li#micropost-#{post.id}", text: post.content)
end
}
end
end
end
end
実行結果 3/4
$ bin/rspec spec/features/users_profile_spec.rb -e "micropost"
UsersProfile
profile
microposts
should eq 30
pagination list each micropost
Finished in 4.77 seconds (files took 1.91 seconds to load)
2 examples, 0 failures
アウトラインの作成 4/4
users_profile_spec.rb
# spec/features/users_profile_spec.rb
RSpec.feature "UsersProfile", type: :feature do
describe "profile"
# (省略)
# フォロー/フォロー解除 ボタン
describe "follow/unfollow buttons"
# フォロー
context "following other-user"
# (自分にとって)following が 1 増加すること (increment: 1)
scenario "following increment 1 (for user)"
# (相手にとって)follower が 1 増加すること (increment: 1)
scenario "followers increment 1 (for other-user)"
# ボタンが Unfollow に変わる
scenario "toggle 'Unfollow'"
# フォロー解除
context "unfollow other-user"
# (自分にとって)following が 1 減少すること (decrement: -1)
scenario "following decrement -1 (for user)"
# (相手にとって)follower が 1 減少すること (decrement: -1)
scenario "followers decrement -1 (for other-user)"
# ボタンが Follow に変わる
scenario "toggle 'Follow'"
end
スペック作成 4/4
users_profile_spec.rb
# spec/features/users_profile_spec.rb
RSpec.feature "UsersProfile", type: :feature do
describe "profile"
before { login_as(user) }
# (省略)
# フォロー/フォロー解除 ボタン
describe "follow/unfollow buttons" do
# フォローする相手のパスに移動
before { visit user_path(other_user) }
# フォロー
context "following other-user" do
subject { click_button "Follow" }
# (自分にとって)following が 1 増加すること (increment: 1)
scenario "following increment 1 (for user)" do
expect { subject }.to change(user.following, :count).by(1)
end
# (相手にとって)follower が 1 増加すること (increment: 1)
scenario "followers increment 1 (for other-user)" do
expect { subject }.to change(other_user.followers, :count).by(1)
end
# ボタンが Unfollow に変わる
scenario "toggle 'Unfollow'" do
expect {
subject
expect(page).to have_css "div#follow_form", text: "Unfollow"
}
end
end
# フォロー解除
context "unfollow other-user" do
before { click_button "Follow" }
subject { click_button "Unfollow" }
scenario "following decrement -1 (for user)" do
expect { subject }.to change(user.following, :count).by(-1)
end
scenario "followers decrement -1 (for other-user)" do
expect { subject }.to change(other_user.followers, :count).by(-1)
end
scenario "toggle 'Follow'" do
expect {
subject
expect(page).to have_css("div#follow_form", text: "Follow")
}
end
end
end
end
end
実行結果 4/4
$ bin/rspec spec/features/users_profile_spec.rb -e "follow/unfollow buttons"
UsersProfile
profile
follow/unfollow buttons
following other-user
following increment 1 (for user)
followers increment 1 (for other-user)
toggle 'Unfollow'
unfollow other-user
following decrement -1 (for user)
followers decrement -1 (for other-user)
toggle 'Follow'
Finished in 5.57 seconds (files took 1.94 seconds to load)
6 examples, 0 failures