2
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?