Ruby
Rails
RSpec
FactoryGirl
テスト

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

<!-- ## フィーチャースペック編 microposts_spec 7/7 -->

テスト対象

該当するコントローラ 該当するアクション
MicropostsController create, destroy

環境

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

フォルダ、使用ファイル

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

アウトラインの作成

  • マイクロポストの作成/削除
  • 他人のマイクロポストは削除できない

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

# spec/features/microposts_pages_spec.rb
RSpec.feature "MicropostsPages", type: :feature do

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

  # マイクロポストの作成(create)
  describe "create"
    # valid な情報の場合
    context "valid info"
      # 成功 (increment: 1)
      it_behaves_like "success create micropost"
      # 成功メッセージ
      scenario "have success messages"
    # invalid な情報の場合
    context "invalid info"
      it_behaves_like "fail create micropost"
      # 失敗メッセージ
      scenario "have error messages"
    # マイクロポストの削除(delete)
    describe "destroy"
      # 自分のマイクロポストの場合
      context "attempt to delete my microposts"
        # 成功 (decrement: -1)
        it_behaves_like "success delete micropost"
        # successメッセージが出力されること
        scenario "have success messages"
      # 他人のマイクロポストの場合
      context "attempt to delete other user's microposts"
        # 失敗 (decrement: 0)
        it_behaves_like "fail delete micropost"
end

スペック作成

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

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

  include SupportModule
  include_context "setup"

  subject { page }

  # マイクロポストの作成(create)
  describe "create" do
    before { login_as(user) }
    # valid な情報の場合
    context "valid info" do
      # 成功 (increment: 1)
      it_behaves_like "success create micropost"
    end
    # invalid な情報の場合
    context "invalid info" do
      it_behaves_like "fail create micropost"
    end
  end

  # マイクロポストの削除(delete)
  describe "destroy", type: :request do
    before { login_as(user) }
    # 自分のマイクロポストの場合
    context "attempt to delete my microposts" do
      it_behaves_like "success delete micropost"
    end
    # 他人のマイクロポストの場合
    context "attempt to delete other user's microposts" do
      it_behaves_like "fail delete micropost"
    end
  end
end

shared_examples の作成

  • it_behaves_like "success create micropost"
  • it_behaves_like "fail create micropost"
  • it_behaves_like "success delete micropost"
  • it_behaves_like "fail delete micropost"
  # spec/support/shared_examples.rb
                    # マイクロポストの作成/削除
    # microposts#create
    # 成功
    shared_examples_for "success create micropost" do
      scenario "micropost increment 1" do
        expect {
          visit root_path
          params = post_params
          fill_in "micropost_content", with: params[:content]
          click_button "Post"
        }.to change(Micropost, :count).by(1)
        # 成功メッセージ
        expect(page).to have_css("div.alert.alert-success", text: "Micropost created")
      end
    end
    # 失敗
    shared_examples_for "fail create micropost" do
      scenario "micropost increment 0" do
        expect {
          visit root_path
          fill_in "micropost_content", with: ""
          click_button "Post"
        }.to change(Micropost, :count).by(0)
        # 失敗メッセージ
        expect(page).to have_css("div.alert.alert-danger")
      end
    end

    # microposts#destroy
    # 成功
    shared_examples_for "success delete micropost" do
      before { my_posts }
      scenario "micropost descrement -1" do
        expect {
          visit root_path
          click_link "delete", match: :first
        }.to change(Micropost, :count).by(-1)
        # 成功メッセージ
        expect(page).to have_css("div.alert.alert-success", text: "Micropost deleted")
      end
    end
    # 失敗
    shared_examples_for "fail delete micropost" do
      before { other_posts }
      scenario "micropost descrement 0" do
        expect {
          # リンクが無いので、直接 HTTPリクエストを発行
          delete micropost_path(other_posts.first.id)
        }.to change(User, :count).by(0)
      end
    end

実行結果

$ bin/rspec spec/features/microposts_pages_spec.rb

MicropostPages
  create
    valid info
      behaves like success create micropost
        micropost increment 1
    invalid info
      behaves like fail create micropost
        micropost increment 0
  destroy
    attempt to delete my microposts
      behaves like success delete micropost
        micropost descrement -1
    attempt to delete other user's microposts
      behaves like fail delete micropost
        micropost descrement 0

Finished in 7.13 seconds (files took 2.15 seconds to load)
4 examples, 0 failures


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

アウトラインの作成

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

  # ページ保護(アクセス権限)
  describe "in MicropostsController"
    # マイクロポスト 投稿(作成)/削除
    describe "authorization of create/destroy micropost"
      context "when non-login"
        describe "create"
        describe "destroy"
end

shared_examples の作成

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

# 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

スペック作成

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

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

  include SupportModule
  include_context "setup"

  # アクセス権限
  describe "in MicropostsController", type: :request do
    # マイクロポスト 投稿(作成)/削除
    describe "authorization of create/destroy micropost" do
      context "when non-login" do
        describe "create" do
          subject { Proc.new { post microposts_path, params: post_params } }
          it_behaves_like "error flash", "Please log in"
          it_behaves_like "redirect to path", "/login"
        end
        describe "destroy" do
          subject { Proc.new { delete micropost_path(my_post.id) } }
          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 "authorization of create/destroy micropost"

Authorization
  in MicropostsController
    authorization of create/destroy micropost
      when non-login
        create
          behaves like error flash
            should eq "Please log in"
          behaves like redirect to path
            should redirect to "/login"
        destroy
          behaves like error flash
            should eq "Please log in"
          behaves like redirect to path
            should redirect to "/login"

Finished in 4.68 seconds (files took 2.33 seconds to load)
4 examples, 0 failures


参考


以上