LoginSignup
3
0

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-08-10

テスト対象

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

環境

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

フォルダ、使用ファイル

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

users_index_spec.rb
# spec/features/users_index_spec.rb
RSpec.feature "UsersIndex", type: :feature do

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

  describe "index"
    # ページネーション
    describe "pagination"
      # ページネーションでユーザが表示されること
      scenario "list each user"

end

スペック作成 1/3

users_index_spec.rb
# spec/features/users_index_spec.rb

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

  include SupportModule
  include_context "setup"

  subject { page }

  describe "index" do
    # ページネーション
    describe "pagination" do
      # (ファクトリをセット)
      before { users }  # => shared_context 内で定義
      # (セットアップの確認)
      it { expect(User.count).to eq users.count }
      # ページネーションでユーザが表示されること
      scenario "list each user" do
        login_as(user)  # => SupportModule 内で定義
        click_link "Users"
        should have_current_path("/users")
        should have_title("All users")
        should have_css("h1", text: "All users")
        User.paginate(page: 1).each do |user|
          expect(page).to have_css("li", text: user.name)
        end
      end
    end
  end
end

shared_context "setup"の作成、ファクトリの作成

  • before { users } の部分
shared_context.rb
  # spec/support/shared_context.rb
  RSpec.shared_context "setup" do

    # 遅延評価、呼ばれた時にDB保存される
    let(:users) { create_list(:other_user, 30) }

  end
users.rb
  # spec/factories/users.rb
  FactoryBot.define do
    # 自分
    # factory [ファクトリ名], class: [クラス名]
    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

SupportModule 作成

  • login_as(user) の部分
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

実行結果 1/3

$ bin/rspec spec/features/users_index_spec.rb

UsersIndex
  index
    pagination
      should eq 30
      list each user

Finished in 5.22 seconds (files took 2.1 seconds to load)
2 examples, 0 failures


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

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

アウトラインの作成 2/3

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 "index"
          # ログインメッセージが出力されること
          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

スペック作成 2/3

  • spec側でブロックをオブジェクト化(Proc.new { })して subject { } に定義
  • get users_path の部分は、直接HTTPリクエストを送るので、オプション type: :request をつけないと NoMethodError になってしまう
authorization_spec.rb
# spec/features/authorization_spec.rb
require 'rails_helper'

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

  describe "in UsersController", type: :request do
    describe "login is necessary" do
      context "when non-login" do
        describe "index" do
          subject { Proc.new { get users_path } }
          it_behaves_like "error flash", "Please log in"
          it_behaves_like "redirect to path", "/login"
        end
      end
    end
  end
end

実行結果 2/3

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

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

Finished in 1.64 seconds (files took 2.31 seconds to load)
2 examples, 0 failures

ユーザ削除権限(admin権限)のスペック

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

アウトライン作成 3/3

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

  describe "in UsersController", type: :request do

    # (省略)

    # ユーザ削除権限
    describe "authorization of delete user"
      # adminユーザの場合
      context "admin"
        # ユーザの削除ができること
        it "success create user"
        # 成功メッセージ
        it "have success messages"
      # 一般ユーザの場合
      context "non-admin"
        # ユーザの削除ができないこと
        it "fail delete user"
        # ルートにリダイレクトしていること
        it "have current path '/'"
end

スペック作成 3/3

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

  include SupportModule     # => login_as(admin) を使う
  include_context "setup"

  describe "in UsersController", type: :request do

    # (省略)

    # ユーザ削除権限
    describe "authorization of delete user" do
      before { users }
      it { expect(User.count).to eq users.count } # (セットアップ確認)
      # adminユーザの場合
      context "admin" do
        it_behaves_like "success delete user"
      end
      # 一般ユーザの場合
      context "non-admin" do
        it_behaves_like "fail delete user"
        it { should have_current_path("/") }
      end
    end
  end
end

shared_examples の作成

  • it_behaves_like "success delete user" の部分を作成
  • it_behaves_like "fail delete user" の部分を作成
shared_examples.rb
  # spec/support/shared_examples.rb

    # ユーザの削除(admin権限)
    # users#destroy

    # 成功
    shared_examples_for "success delete user" do
      scenario "user decrememt -1" do
        login_as(admin)
        click_link "Users"
        expect(page).to have_current_path("/users")
        expect(page).to have_link('delete', href: user_path(User.first))
        expect(page).to have_link('delete', href: user_path(User.second))
        expect(page).not_to have_link('delete', href: user_path(admin))
        expect {
          click_link('delete', match: :first)
          # 成功メッセージ
          expect(page).to have_css("div.alert.alert-success", text: "User deleted")
        }.to change(User, :count).by(-1)
      end
    end

    # 失敗
    shared_examples_for "fail delete user" do
      scenario "user decrement 0" do
        login_as(user)
        click_link "Users"
        expect(page).to have_current_path("/users")
        expect(page).not_to have_link('delete', href: user_path(User.first))
        expect(page).not_to have_link('delete', href: user_path(User.second))
        expect {
          # リンクが無いので、直接 HTTPリクエストを発行
          delete user_path(User.first)
        }.to change(User, :count).by(0)
      end
    end

実行結果 3/3

$ bin/rspec spec/features/authorization_spec.rb -e "authorization of delete user"

Authorization
  in UsersController
    authorization of delete user
      should eq 30
      admin
        behaves like success delete user
          user decrememt -1
      non-admin
        should have current path "/"
        behaves like fail delete user
          user decrement 0

Finished in 8.17 seconds (files took 2.23 seconds to load)
4 examples, 0 failures


参考


続く

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

3
0
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
3
0