LoginSignup
2
5

More than 5 years have passed since last update.

Railsチュートリアル テストをRSpecで実施 【フィーチャスペック編 users_profile_spec 4/7】

Last updated at Posted at 2018-08-10

テスト対象

該当するコントローラ 該当するアクション
UsersController show

環境

Userモデル 単体テスト編 1/3 に記載

フォルダ、使用ファイル

種類 ファイル名
スペック 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

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


参考


続く

フィーチャスペック編 users_edit_spec 5/7

2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5