カテゴリー:
Rails
タグ:
 Rspec Rails4 receive and_call_original

このエントリーをはてなブックマークに追加
更新日時:
2013年08月06日(火)
作成日時:
2013年08月06日(火)

前の記事 / 次の記事

Rails4のsacaffoldでコントローラーに生成される
before_action :set_xxx をテストしてみようとしたら嵌まったのでメモ。

Rspecの receive はあるメソッドが実行されるかどうかを調べるモック。

でも、あるメソッドが実行されるかどうかを調べるモックなので、
実行されることが確定したらモックは成功したことを返してその内容は実行されないで終わる。

けど、実行させたい話。

例えばコントローラーが

class ArticlesController < ApplicationController
  before_action :set_article、only: [:show, :edit, :update, :destroy]

  def show
  end

  private
    def set_article
      @article = Article.find(params[:id])
    end
end

みたいになっている時に、

it "excutes before_action :set_article" do
  article = Article.create! valid_attributes
  expect(subject).to receive(:set_article)
  get :show, { id: article.to_param}, valid_session
end

と書くと、showアクションが実行される時は :set_article フィルターを通るので、
このテストは成功する。

でも、

it "excutes before_action :set_article" do
  article = Article.create! valid_attributes
  expect(subject).to receive(:set_article)
  patch :update, { id: article.to_param, article: valid_attributes }, valid_session
end

と、updateアクションが実行される時に :set_article フィルターを通るかどうか確認するテストを書いた時、このテストは、
:set_article フィルターを通るのが確定した時点でモックが成功したことを返してその中身が実行されずに終了してしまうので、
updateすべき @article が見つからずに(NilClassをupdateしようとして)NomethodError を吐いてこのテストは失敗する。

どうしたらいいのか?
rspec/rspec-mocks Delegating to the Original Implementation

expect(subject).to receive(:set_article).and_call_original

とすることで、フィルターを通ることを確認することに加えて、その中身を実行することができる。
なので、さっきのテストは次のように書き直すことで成功する。

it "excutes before_action :set_article" do
  article = Article.create! valid_attributes
  expect(subject).to receive(:set_article).and_call_original
  patch :update, { id: article.to_param, article: valid_attributes }, valid_session
end

ていうか今までたまたま問題にならなくて気付かなかったけど、
このモックってreceiveが確認されたら中身は実行されないで終了するとか知らなかった。