Ruby
Rails
RSpec
テスト

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

テスト対象

該当するコントローラ 該当するアクション
UsersController following, followers

環境

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

フォルダ、使用ファイル

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

アウトライン作成 1/2

  • フォロー/フォロワー のユーザ情報の表示

  • ※ 未ログインの場合(ページ保護)/ユーザ削除権限(admin権限)のテストは authorization_spec.rb (別ファイル)にて作成
    フィーチャスペック編 authorization_spec

users_following_spec.rb
# spec/features/users_following_spec.rb
RSpec.feature "UsersFollowing", type: :feature do

  # 未ログイン場合のスペックは、
  # authorization_spec.rb にて(別ファイル)作成

  # フォロー/フォロワー
  describe "following/followers method"
    # 自分がフォロー中のユーザのページ
    describe "following page"
      # ユーザ情報の表示が正しいこと
      it_behaves_like "have user infomation"
    # 自分をフォローしているユーザのページ
    describe "followers page"
      # ユーザ情報の表示が正しいこと
      it_behaves_like "have user infomation"

end

shared_examples の作成

  • ##### it_behaves_like "have user infomation"
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

スペック作成 1/2

users_following_spec.rb
# spec/features/users_follow_spec.rb

require 'rails_helper'

RSpec.feature "UsersFollow", type: :feature do

  include SupportModule
  include_context "setup"

  subject { page }

  # 未ログインの場合のテストは、
  # authorization_spec.rb にて(別ファイル)実施

  describe "following/followers method" do
    describe "following page" do
      before do
        login_as(user)
        click_link "following"
      end
      it_behaves_like "have user infomation"
    end
    describe "followers page" do
      before do
        login_as(user)
        click_link "following"
      end
      it_behaves_like "have user infomation"
    end
  end
end

実行結果 1/2

$ bin/rspec spec/features/users_follow_spec.rb -e "following page" -e "followers page"

UsersFollow
  following/followers method
    following page
      behaves like have user infomation
        should have visible css "img.gravatar"
        should have visible css "h1" with text "Example user"
        should text "Microposts"
        should have visible link "view my profile" with href "/users/5"
    followers page
      behaves like have user infomation
        should have visible css "img.gravatar"
        should have visible css "h1" with text "Example user"
        should text "Microposts"
        should have visible link "view my profile" with href "/users/5"

Finished in 5.6 seconds (files took 2.16 seconds to load)
8 examples, 0 failures


アウトライン作成 2/2

users_following_spec.rb
# spec/features/users_follow_spec.rb
RSpec.feature "UsersFollowing", type: :feature do

  # フォロー/フォロワー
  describe "following/followers method"

    # (省略)

    # ページネーション
    describe "pagination"
      # ユーザ情報が表示されている
      scenario "list each following"
      # ユーザ情報が表示されている
      scenario "list each followers"

end

サンプルデータのセットアップ

users_following_spec.rb
# spec/features/users_follow_spec.rb
  describe "following/followers method" do
    # セットアップ
    before do
      users # => shared_context 内で定義 create_list(:other_user, 30)
      users.each do |u|
         user.follow(u) # => 自分が 30人をフォローする
         u.follow(user) # => 他人の 30人にフォローされる
      end
      user.follow(other_user) # => 自分が他人(1人)をフォロー
      other_user.follow(user) # => 他人が自分(1人)をフォロー
    end
    # (セットアップの確認:31人をフォローしている)
    it { expect(user.following.count).to eq 31 }
    # (セットアップの確認:31人にフォローされている)
    it { expect(other_user.following.count).to eq 1 }
    it { expect(user.followers.count).to eq 31 }
  end

コンソールで確認

  • $rails console test --sandbox
  (抜粋)
  $ rails c test -s
  [1] pry(main)>
  [2] pry(main)> users = FactoryBot.create_list(:other_user, 3)
    id: 5,
    name: "Jess Witting",
    email: "everettkunze@wintheiser.com",

    id: 6,
    name: "Tula Reilly",
    email: "sashagleason@leuschke.co",

    id: 7,
    name: "Cori Emard",
    email: "rustywill@mayert.com",

  [3] pry(main)>
  [4] pry(main)>
  [5] pry(main)> me = FactoryBot.create(:user)
   id: 8,
   name: "Example user",
   email: "user@example.com",

  [8] pry(main)>
  [9] pry(main)> users.each do |u|
  [9] pry(main)*   me.follow(u)
  [9] pry(main)*   u.follow(me)
  [9] pry(main)* end

  (抜粋)自分が フォロー中のユーザ

    [["follower_id", 8], ["followed_id", 5], ["created_at", "2018-08-07 02:07:11.485562"], ["updated_at", "2018-08-07 02:07:11.485562"]]

    [["follower_id", 8], ["followed_id", 6], ["created_at", "2018-08-07 02:07:11.512676"], ["updated_at", "2018-08-07 02:07:11.512676"]]

    [["follower_id", 8], ["followed_id", 7], ["created_at", "2018-08-07 02:07:11.536817"], ["updated_at", "2018-08-07 02:07:11.536817"]]

  (抜粋)自分を フォローしているのユーザ

    [["follower_id", 5], ["followed_id", 8], ["created_at", "2018-08-07 02:07:11.499536"], ["updated_at", "2018-08-07 02:07:11.499536"]]

    [["follower_id", 6], ["followed_id", 8], ["created_at", "2018-08-07 02:07:11.527219"], ["updated_at", "2018-08-07 02:07:11.527219"]]

    [["follower_id", 7], ["followed_id", 8], ["created_at", "2018-08-07 02:07:11.546484"], ["updated_at", "2018-08-07 02:07:11.546484"]]

実行結果

  • サンプルデータのセットアップ
  $ bin/rspec spec/features/users_follow_spec.rb

  UsersFollow
    following/followers method
      should eq 31
      should eq 1
      should eq 31

  Finished in 3.87 seconds (files took 2.08 seconds to load)
  3 examples, 0 failures

スペック作成 2/2

users_following_spec.rb
# spec/features/users_following_spec.rb
require 'rails_helper'

RSpec.feature "UsersFollow", type: :feature do

  include SupportModule
  include_context "setup"

  subject { page }

  # 未ログインの場合のテストは、
  # authorization_spec.rb にて(別ファイル)実施

  describe "following/followers method" do
    before do
      users # => shared_context 内で定義 create_list(:other_user, 30)
      users.each do |u|
         user.follow(u) # => 自分が 30人をフォローする
         u.follow(user) # => 他人の 30人にフォローされる
      end
      user.follow(other_user) # => 自分が他人(1人)をフォロー
      other_user.follow(user) # => 他人が自分(1人)をフォロー
    end

    # (セットアップの確認:31人をフォローしている)
    it { expect(user.following.count).to eq 31 }
    # (セットアップの確認:31人にフォローされている)
    it { expect(other_user.following.count).to eq 1 }
    it { expect(user.followers.count).to eq 31 }

    # (省略)

    describe "pagination" do
      before { login_as(user) }
      scenario "list each following" do
        # login_as(user)
        click_link "following"
        user.following.paginate(page: 1).each do |u|
          expect(page).to have_css("li", text: u.name)
          expect(page).to have_link(u.name, href: user_path(u))
        end
      end
      scenario "list each followers" do
        # login_as(user)
        click_link "followers"
        user.followers.paginate(page: 1).each do |u|
          expect(page).to have_css("li", text: u.name)
          expect(page).to have_link(u.name, href: user_path(u))
        end
      end
    end
  end
end

実行結果 2/2

$ bin/rspec spec/features/users_follow_spec.rb -e "pagination"

UsersFollow
  following/followers method
    pagination
      list each following
      list each followers

Finished in 6.39 seconds (files took 1.98 seconds to load)
2 examples, 0 failures


未ログインの場合(ページ保護)のスペック

  • #### authorization_spec.rb (別ファイル)にて作成

アウトラインの作成

authorization_spec.rb
# spec/features/authorization_spec.rb
RSpec.feature "Authorization", type: :feature do

  # ページ保護(アクセス権限)
  describe "in UsersController"
    describe "login is necessary"
      # 未ログインの場合
      context "when non-login"
        describe "edit"
          # ログインメッセージが出力されること
          it "should have error_messages 'Please log in'"
          # ログインページへリダイレクトされること
          it "should have redirect to '/login'"
        describe "update"
          it "should have error_messages 'Please log in'"
          it "should have redirect to '/login'"
end

shared_examples の作成

  • ログインメッセージ出力・ログインページへリダイレクトは、一般化して使い回せるようにしてみる
  • spec側でブロックをオブジェクト化(Proc.new { })して subject { } に定義し、shared_examples側で、subject.call して呼び出す

shared_examples.rb
  # spec/support/shared_examples.rb

    # 成功メッセージ
    # flash[:success]
    shared_examples_for "success message" do |msg|
      it { subject.call; expect(flash[:success]).to eq msg }
    end

    # 失敗メッセージ
    # flash[:danger]
    shared_examples_for "error message" do |msg|
      it { subject.call; expect(flash[:danger]).to eq msg }
    end

    # リダイレクト
    # redirect to path
    shared_examples_for "redirect to path" do |path|
      it { subject.call; expect(response).to redirect_to path }
    end

スペック作成

authorization_spec.rb
# spec/features/authorization_spec.rb
RSpec.feature "Authorization", type: :feature do

  include SupportModule
  include_context "setup"

  # アクセス権限
  describe "in UsersController", type: :request do
    # 未ログインの場合 (before_action のテスト)
    describe "login is necessary" do
      context "when non-login" do
        describe "following" do
          subject { Proc.new { get following_user_path(user) } }
          it_behaves_like "error flash", "Please log in"
          it_behaves_like "redirect to path", "/login"
        end
        describe "followers" do
          subject { Proc.new { get followers_user_path(user) } }
          it_behaves_like "error flash", "Please log in"
          it_behaves_like "redirect to path", "/login"
        end
      end
    end
  end
end

実行結果

$ bin/rspec spec/features/authorization_spec.rb -e "following" -e "followers"

Authorization
  in UsersController
    login is necessary
      when non-login
        following
          behaves like error flash
            should eq "Please log in"
          behaves like redirect to path
            should redirect to "/login"
        followers
          behaves like error flash
            should eq "Please log in"
          behaves like redirect to path
            should redirect to "/login"

Finished in 1.86 seconds (files took 1.95 seconds to load)
4 examples, 0 failures


参考


続く

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