テスト対象
該当するコントローラ | 該当するアクション |
---|---|
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.rb
spec/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