Railsのテストを書く上で,やっぱりRSpecはめちゃくちゃ書きやすい.
そんな中でも最近はcontrollerのspecはあまり書かなくて,requestのspecばかりを書いている.
そもそもcontrollerのspecは単体テストで,requestのspecは結合テストなのだけれど,だいたい同じ部分のテストになってしまうので,片方だけ書いたら割りと満足しています.
というわけでrequestのテストを書いてみます.
ちなみにFactoryGirlを使って色々生成しているので,その部分のfactoryは書いてあるものとします.
scaffoldで生成されるデフォルトのアクション
まずは普通に生成されるアクションについてのテストです.
StaticController
について,index,show,new,edit,create,update,destroyのそれぞれのアクションのテストを書きます.
require 'rails_helper' RSpec.describe "Statics", type: :request do include Rack::Test::Methods before(:each) do @static = FactoryGirl.create(:static) @params = { static: FactoryGirl.attributes_for(:static) } end describe "GET /statics" do it "works!" do get statics_path expect(last_response.status).to eq(200) end end describe "GET /statics/new" do it "works!" do get new_static_path expect(last_response.status).to eq(200) end end describe "GET /statics/1/edit" do it "works!" do get edit_static_path(@static) expect(last_response.status).to eq(200) end end describe "POST /statics/create" do it "should create" do post statics_path, @params ## 成功した場合デフォルトではredirect_toされる ## 成功したかの確認はリダイレクトとerrorsの中身で確認する expect(last_response.status).to eq(302) expect(last_response.errors).to eq("") end end describe "PUT /statics/1/update" do it "should update" do put static_path(@static), @params expect(last_response.status).to eq(302) expect(last_response.errors).to eq("") end end describe "DELETE /statics/1/destroy" do it "should delete" do delete static_path(@static) expect(last_response.status).to eq(302) expect(last_response.errors).to eq("") end end end
コントローラをscaffoldで生成した場合,create,update,destroyアクションは,成功時にリダイレクトをかけます.
なのでstatusが302になっているのが正しい挙動です.
デフォルトで生成されるrequestのspecでは,last_responseではなく,responseで判定しています.
expect(response).to have_http_status(200)
これでもいいのですが,postやputアクションのときに困ります.
成功時にリダイレクトされるので,statusは302になるのですが,それだけでチェックするのがちょっと不安…….
というわけでerrorsの中身か,もしくはbodyの中身をチェックしたいのです.
statusが200のときは,response.body
を見ればいいだけの話ですが,statusが302のときはbodyにはYou are being redirected
が入っているだけです.
そのため,
include Rack::Test::Methods
として,テストメソッドを別途読み込みし,last_responseが使えるようにしています.
last_responseには,errorsが含まれるので,bodyがチェックできない代わりにerrorsの中身をチェックして満足することにしました.
ログインが必要になるコントローラのテスト
ログインが必要になる場合には,予めログインさせておいた上で,上記のようなテストを組む必要があります.
まず,ログイン用のメソッドを作っておきます.
ちなみにログインするモデルはUserとしてあります.
/spec/support/request_helper.rb
include Warden::Test::Helpers module RequestHelpers def login_user(user) login_as user, scope: :user end end
追加したログイン用メソッドを読み込む必要があるため,rails_helper.rbに追記します.
/spec/rails_helper.rb
# 中略 Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } RSpec.configure do |config| ## 中略 config.include Devise::TestHelpers, :type => :controller config.include RequestHelpers, :type => :request config.include Capybara::DSL end
そしてテスト本体を書きます.
/spec/requests/users/statics_spec.rb
require 'rails_helper' RSpec.describe "Users::StaticsController", type: :request do describe "before login" do describe "GET /users/statics" do it "should redirect" do get users_statics_path expect(response).to have_http_status(302) end end end describe "after login" do before(:each) do @user = FactoryGirl.create(:user) login_user @user end describe "GET /users/statics" do it "works!" do get users_statics_path expect(response).to have_http_status(200) end end # 残りは省略 end end
こんな感じで,ログイン前にはsign_inにリダイレクトがかかるため,そのテストをしています.
ログイン後は,同じアドレスでアクセスできることを確認しています.
こちらもpostやputのテストをする場合は,
include Rack::Test::Methods
して,last_responseを使えるようにします.