LoginSignup
62
62

More than 5 years have passed since last update.

before_filterを分離してテストする方法(Rspec)

Last updated at Posted at 2013-06-27

Controllerのテストを書く場合、before_filterに惑わされることが多々ありました。
そこで、before_filter用のメソッドのテストとController上のactionのテストを分離する方法です。

環境

  • ruby: 2.0.0-p0
  • rails: 4.0.0.rc1
  • rspec: 2.13.1

事前準備

Anonymous Controllerを有効にしておきましょう。

spec/spec_helper.rb
config.infer_base_class_for_anonymous_controllers = true

実装とテスト

app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :load_user
  def index
    @name = @user.name
  end

  private
  def load_user
    @user = User.first || raise("User does not exist.")
  end
end
spec/controllers/users_controller_spec.rb
require 'spec_helper'

describe UsersController do

  describe "GET 'index'" do
    before do
      controller.stub(:load_user).and_return(true)
      @user = User.create :name => "hoge"
      controller.instance_variable_set(:@user, @user)
    end

    it "リクエストに成功する" do
      get :index
      response.should be_success
    end

    it "必要なデータが帰ってくる" do
      get :index
      expect(assigns[:name]).to eq("hoge")
    end
  end

  describe :load_user do
    controller do
      def index
        render :text => "ok!"
      end
    end

    it "Userがない場合にerror" do
      User.delete_all
      expect{ get :index }.to raise_error
    end

    it "Userがある場合に'ok!'がレンダリングされる" do
      @user = User.create :name => "hoge"
      get :index
      assigns[:user].should eq(@user)
      response.body.should == "ok!"
    end
  end
end

ポイント

[describe "GET 'index'"]にてindexアクションのテストをし、[describe :load_user]でbefore_filterのテストをしています。

indexアクション

stubを使ってbefore_filterで呼び出される処理を上書きしています。

controller.stub(:load_user).and_return(true)

before_filterで設定していた内容を手動で与えます。

@user = User.create :name => "hoge"
controller.instance_variable_set(:@user, @user)

あとは、純粋にindexメソッドをテストします。

before_filter

Anonimous Controllerでダミーのコントローラを作成します。

controller do
  def index
    render :text => "ok!"
  end
end

あとは、before_filterに引っかかってエラーやリダイレクトが発生するかやインスタンス変数に値が格納されているか等をテストします。

まとめ

この方法を使用すれば、複数の場所で使用されているbefore_filterのテストを一箇所にまとめることができるかなと思います。

修正1

skip_before_filterを呼ぶより、普通にstubを使ったほうがシンプルで安全なので修正しました。

修正前
before do
  controller.class.skip_before_filter :load_user
end
after do
  controller.class.before_filter :load_user
end
修正後
before do
  controller.stub(:load_user).and_return(true)
end

修正2

instance_evalよりもinstance_variable_setを使うほうがいいですね。
インスタンス変数に設定している@userをテスト中に使用できる等の利点があります。

修正前
controller.instance_eval do
  @user = User.create :name => "hoge"
end
修正後
@user = User.create :name => "hoge"
controller.instance_variable_set(:@user, @user)
62
62
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
62
62