Ruby
RSpec

rspec-mocks の and_raise が ArgumentError になってしまう場合は例外クラスに引数を渡す

こういうテストがあるとする

spec/my_spec.rb
require 'spec_helper'
require 'rack-timeout'

class Klass
end

describe 'Rack::Timeout::RequestTimeoutException' do
  it 'bad' do
    expect(Klass).to receive(:new).and_raise(Rack::Timeout::RequestTimeoutException)
    expect {
      Klass.new
    }.to raise_error(Rack::Timeout::RequestTimeoutException)
  end

  it 'good' do
    expect(Klass).to receive(:new).and_raise(Rack::Timeout::RequestTimeoutException.new( {} ))
    expect {
      Klass.new
    }.to raise_error(Rack::Timeout::RequestTimeoutException)
  end
end

実行するとこうなる

$ rake

Rack::Timeout::RequestTimeoutException
  bad (FAILED - 1)
  good

Failures:

  1) Rack::Timeout::RequestTimeoutException bad
     Failure/Error:
       expect {
         Klass.new
       }.to raise_error(Rack::Timeout::RequestTimeoutException)

       expected Rack::Timeout::RequestTimeoutException, got #<ArgumentError: wrong number of arguments (given 0, expected 1)> with backtrace:
         # ./spec/my_spec.rb:11:in `block (3 levels) in <top (required)>'
         # ./spec/my_spec.rb:10:in `block (2 levels) in <top (required)>'
     # ./spec/my_spec.rb:10:in `block (2 levels) in <top (required)>'

Finished in 0.0397 seconds (files took 0.28993 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./spec/my_spec.rb:8 # Rack::Timeout::RequestTimeoutException bad

Rack::Timeout::RequestTimeoutException#initialize は引数を一つとるが and_raise に例外クラスそのものを渡すと new するとき引数を渡してくれないので ArgumentError がでてしまう。
https://github.com/heroku/rack-timeout/blob/bd7b37117867e224ed68c8cc9eed6256203ab169/lib/rack/timeout/core.rb#L12-L17

適当な引数を渡して作ったインスタンスを and_raise に渡せば、意図した通りに動く。


参考(っていうかこのブログ記事のほうがより詳しい)
https://tnakamura.hatenablog.com/entry/2015/02/02/rspec_mocks_and_raise