LoginSignup
3
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-08-10

テスト対象

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

環境

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

フォルダ、使用ファイル

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

users_edit_spec.rb
# spec/features/users_edit_spec.rb

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

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

  describe "edit"
    # 情報が valid の場合
    context "valid info"
      scenario "success edit profile"
    # 情報が invalid の場合
    context "invalid info"
      scenario "fail edit profile"

end

スペック作成 1/2

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

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

  include SupportModule
  include_context "setup"

  subject { page }

  describe "edit" do
    # 情報が valid の場合
    context "valid info" do
      scenario "success edit profile" do
        login_as(user)
        click_link "Settings"
        should have_title("Edit user")
        should have_css("h1", text: "Update your profile")
        should have_link("change", href: "http://gravatar.com/emails")
        expect {
          fill_in_update_profile_form("New Name", "new@example.com")
          click_button "Save changes"
        }.to change(User, :count).by(0)
        expect(user.reload.name).to eq "New Name"
        expect(user.reload.email).to eq "new@example.com"
        success_messages("Profile updated")
      end
    end
    # 情報が invalid の場合
    context "invalid info" do
      scenario "fail edit profile" do
        login_as(user)
        click_link "Settings"
        expect {
          fill_in_update_profile_form("", "foo@")
          click_button "Save changes"
        }.to change(User, :count).by(0)
        expect(user.reload.name).not_to eq ""
        expect(user.reload.email).not_to eq "foo@"
        error_messages("errors")
      end
    end
  end
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
    end
  end

SupportModule の作成

↓の部分をメソッドとして定義

  • fill_in_update_profile_form
  • success_messages("Profile updated")
  • error_messages("errors")
support_module.rb
  # spec/support/support_module.rb
  module SupportModule

    def fill_in_update_profile_form(name, email, password = "", confirmation = "")
      fill_in "Name",         with: name
      fill_in "Email",        with: email
      fill_in "Password",     with: password
      fill_in "Confirmation", with: confirmation
    end

    def success_messages(msg)
      expect(page).to have_css("div.alert.alert-success", text: msg)
    end

    def error_messages(msg = "")
      if msg.empty?
        should have_css('div.alert.alert-danger')
      else
        should have_css('div.alert.alert-danger', text: msg)
      end
    end
  end

実行結果 1/2

$ bin/rspec spec/features/users_edit_spec.rb -e "info"

UsersEdit
  edit
    valid info
      success edit profile
    invalid info
      fail edit profile

Finished in 4.96 seconds (files took 2.3 seconds to load)
2 examples, 0 failures


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

users_edit_spec.rb
# spec/features/users_edit_spec.rb
RSpec.feature "UsersEdit", type: :feature do

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

  describe "edit"

    # (省略)

    # admin属性
    describe "admin-attribute"
      # admin属性をweb経由で変更できないこと
      scenario "not allow change via the web"
    # フレンドリーフォワーディング
    describe "friendly forwarding"
      # ログイン後は、
      context "after login (non-login-user)"
        # 目的としていたページに遷移していること
        scenario "render desired page"
end

スペック作成 2/2

  • patch user_path(user), params: { user: admin_params } の部分は、直接HTTPリクエストを送るので、オプション type: :request をつけないと NoMethodError になってしまう
users_edit_spec.rb
# spec/features/users_edit_spec.rb
RSpec.feature "UsersEdit", type: :feature do

  describe "edit"

    # (省略)

    # admin属性
    describe "admin-attribute", type: :request do
      # web経由で変更できないこと
      scenario "not allow change via the web" do
        patch user_path(user), params: { user: admin_params }
        expect(user.reload).not_to be_admin
        should have_current_path("/")
      end
    end

    # フレンドリーフォワーディング
    describe "friendly forwarding" do
      # ログイン後は、
      context "after login (non-login-user)" do
        # 目的としていたページに遷移していること
        scenario "render desired page" do
          visit edit_user_path(user)
          error_messages("Please log in")
          should have_current_path("/login")
          login_as(user)
          should have_current_path(edit_user_path(user))
          should have_title("Edit user")
          should have_css("h1", text: "Update your profile")
        end
      end
    end
  end
end

shared_context の作成

shared_context.rb
  # spec/support/shared_context.rb
  RSpec.shared_context "setup" do

    # 属性をハッシュ化して呼ばれた時に使う
    let(:admin_params) { attributes_for(:user, admin: true) }

  end

ファクトリの使い方

FactoryBot.attributes_for(:user, admin: true)
  • 属性値をハッシュ化する
  $ rails console test --sandbox

  [1] pry(main)>
  [2] pry(main)> params = FactoryBot.attributes_for(:user, admin: true)
  => {:name=>"Example user",
   :email=>"user@example.com",
   :password=>"foobar",
   :password_confirmation=>"foobar",
   :admin=>true}

実行結果 2/2

$ bin/rspec spec/features/users_edit_spec.rb -e "admin-attribute"

UsersEdit
  edit
    admin-attribute
      not allow change via the web

Finished in 1.58 seconds (files took 2.02 seconds to load)
1 example, 0 failures

$ bin/rspec spec/features/users_edit_spec.rb -e "friendly forwarding"

UsersEdit
  edit
    friendly forwarding
      after login (non-login-user)
        render desired page

Finished in 3.96 seconds (files took 2.09 seconds to load)
1 example, 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_expamples.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 "edit" do
          subject { Proc.new { get edit_user_path(user) } }
          it_behaves_like "error flash", "Please log in"
          it_behaves_like "redirect to path", "/login"
        end
        describe "update" do
          subject { Proc.new { patch user_path(user), params: { user: update_params_1 } } }
          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 "edit" -e "update"

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

Finished in 2.11 seconds (files took 2.38 seconds to load)
4 examples, 0 failures


参考


続く

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

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