yamotonalds's blog

Webアプリケーション開発における技術メモが中心です。たまにWebサービス、興味を持ったデバイス、自作PCに関する話題もあるかも。Amazon好きなのでAmazon.co.jpアソシエイト使ってます。

rspec-rails3.0でのControllerテストの書き方

Controllerテストの書き方のメモ。

通常のページ

require 'spec_helper'

describe HogesController do
  describe "GET show" do
    let(:request) { get :show, id: 1 }

    subject do
      request
    end

    it { should be_ok }
    it { should render_template "show" }

    describe "@hoge" do
      subject do
        request
        assigns(:hoge)
      end

      it { should be_present }
    end
  end

  describe "DELETE destroy" do
    let(:request) { delete :destroy, id: 1 }

    before do
      @hoge = stub_model(Hoge, destroy: nil)
      allow(Hoge).to receive(:find).and_return(@hoge)
    end

    subject do
      request
    end

    it { should be_success }
    it { should redirect_to hoges_path)

    describe "@hoge" do
      subject do
        request
        assigns(:hoge) 
      end

      it { should be_present }
      it "#destory called once" do
        expect(@hoge).to receive(:destroy).once

        subject
      end
    end
  end
end

getpostResponseオブジェクトを返すのでそれをテストする。
ロジックの呼び出しに関してはモックでテスト。

ステータスコードは、

its(:status) { should eq 200 }
# または
its(:status) { should eq Rack::Utils.status_code(:ok) }

でも確認できるが可読性やタイプ数を考えるとbe_*で済むものは済ませたい。
使いそうなのは、

  • be_ok # 200
  • be_success # 200番台
  • be_successful # 200番台
  • be_redirect # 301, 302, 303, 307のどれか
  • be_forbidden # 403
  • be_not_found # 404
  • be_missing # 404

あたりか。

アサインした変数と意図したテンプレートが使われたかはチェックするがそのレンダリング内容についてはViewのテストで行うのでここではテストしない。

@hogeFactoryGirl.create(:hoge)でも良いのだがstub_modelの方がテストの実行時間が短くなるので積極的に利用する(そもそもコントローラのテストは本質的にはDBは不要)。

※2014-03-07 stubを使う部分のコードがmochaのものになっていたのでrspecのものに修正
mochaをインストールしていたせいかspec_helper.rbでmochaを使う設定にしていないのにstubsreturnsを使っていても表面上はエラーにならなかった。
しかし、specファイルを指定して実行してもエラーにならないがall testだとエラーになる現象が発生するようになった(100%ではなくall testが通る場合もある)。調べるとあるspecファイルで使ったstubsが他のspecファイルでも有効になっていた(そのためテストの実行順によってはall testが通った)。

Deviseを使う場合

spec/support/devise.rbを以下の内容で作成。

RSpec.configure do |config|
  config.include Devise::TestHelpers, type: :controller
end

これでsign_in, sign_outが使える。

また、DeviseControllerを継承したコントローラのテストではリクエスト前に

@request.env["devise.mapping"] = Devise.mappings[:user]

が必要になる。

Ajaxを使う場合

Viewのlink_to等でremote: trueとした場合、.jsがリクエストされるのでテストでは次のようにリクエストする。

delete :destroy, id: 1, format: :js

これで406 Not Acceptableになるようならアクションのrespond_toformat.jsが書かれていないと思われる。
また、401になる場合はログイン状態、403になる場合はCanCan等承認周りを確認する。