経緯
こちら側でAPI作ってたのをテストしたかった感じです。
今回は他の例外もキャッチするパターンも用意してました。
ただ、どれにも該当しない500エラー(InternalServerError)の発生方法に躓いて悩んでいました。
↓みたいな
rescue 他エラークラス => e
render :json => { 'res': 'ng' }, status: 400
rescue => e # <= こいつ
Rails.logger.debug(e.backtrace.join("\n"))
render :json => { 'res': 'ng' }, status: 500
※他にもrescueパターンや共通化とかしてた部分とかは省略。
結論
対象のController#Methodをスタブ化すれば良かっただけでした。
(エラー箇所は変えて大丈夫かと。今回は500が欲しかったのでStandardErrorにしました)
※非推奨の部分は ちょっと調べてみた を見てください
allow_any_instance_of(HogeController).to receive(:method).and_raise(StandardError.new(nil))
# 非推奨なのでヤメました → HogeController.any_instance.stub(:method).and_raise(StandardError.new(nil))
e.g.
renderされるJSON
# 成功(200)
{ 'res': 'ok' }
# 失敗(500)
{ 'res': 'ng' }
controller
class Api::V1::HogeController < Api::V1::BaseController
def test
.
.
render :json => { 'res': 'ok' }, status: 200
rescue => e
render :json => { 'res': 'ng' }, status: 500
end
end
request spec
require 'rails_helper'
describe Api::V1::HogeController, type: :request do
subject { post '/api/v1/hoge/test' }
it 'ok' do
subject
expect(response.status).to eq 200
expect(response.body).to eq { 'res': 'ok' }
end
it 'ng' do
allow_any_instance_of(Api::V1::HogeController,).to receive(:method).and_raise(StandardError.new(nil))
subject
expect(response.status).to eq 500
expect(response.body).to eq { 'res': 'ng' }
end
end
参照
GitLabのgoogle_api/authorizations_controller_spec.rb から パクってきた ヒント得ました。
あと、Controllerのスタブ化で少し悩んだので こちらの方のブログ も参考にしました。
ありがとうございました。
ちょっと調べてみた
同じように悩んでる人いました。
https://cloud6.net/so/ruby-on-rails/3142894
ただ調べていくとどうも any_instance.stub
は非推奨みたいです。
その代わりに allow_any_instance_of
を使うことに。
ただ、 どうやら allow_any_instance_of
も非推奨みたいです...
-> https://github.com/rspec/rspec-mocks#settings-mocks-or-stubs-on-any-instance-of-a-class
・The rspec-mocks API is designed for individual object instances, but this feature operates on entire classes of objects. As a result there are some semantically confusing edge cases. For example in expect_any_instance_of(Widget).to receive(:name).twice it isn't clear whether each specific instance is expected to receive name twice, or if two receives total are expected. (It's the former.)
・Using this feature is often a design smell. It may be that your test is trying to do too much or that the object under test is too complex.
・It is the most complicated feature of rspec-mocks, and has historically received the most bug reports. (None of the core team actively use it, which doesn't help.)
範囲が広いしテストが複雑化してるからテストケースの根本的な問題を直せ、的な?
バグissueも歴史的に多くあるらしい、のであまり使わない方が良いとのこと。
そこで allow_any_instance_of
を使わない方向で調査してみました。
instance_double
と allow
で対応されてる方が多かったです。
確かにController#Method はインスタンスメソッドだと考え下記のように書きました。
hoge_mock = instance_double(Api::V1::HogeController)
allow(Api::V1::HogeController).to receive(:new).and_return(hoge_mock)
allow(hoge_mock).to receive(:test).and_raise(StandardError.new(nil))
post '/api/v1/hoge/test'
→ ダメでした。
ちょっと端折りますが↓のようなエラーです。
#<InstanceDouble(Api::V1::HogeController) (anonymous)> received unexpected message :dispatch with ("test", #...)
以下独り言です。
憶測ですけど、 :new
と :test
だけスタブ化したところで、Railsの動き的にController#Methodが呼ばれるまでに処理される内容(他の呼び出されるメソッド)を用意してなくてダメ?な気がします。
, but this feature operates on entire classes of objects.
と、 rspec-mocks
のREADMEに書いてある通り、 「この機能はオブジェクトのクラス全体に作用します」
とのこと。
なので動いていたのかな、とか考えてたりします。
この辺りまだ調べきれていないので分かる人いたら教えていただけますと嬉しいです。