RSpecでエラーを発生させたいのにnothing was raisedと出る


システムをrubyのバージョンをアップ（ruby 1.9.2p320⇒2.2.3）しました。Railsのバージョンは3.2.19
DBはMySQLを使用しています。  
テストを実行したところエラーが発生し以下のようなエラーが発生しました。

[code]

    Failures:
    
      1) …の場合は、 そのまま例外をraiseする。
         Failure/Error: lambda{Job.find_by_id(@conn, 0)}.should raise_error(ex)
           expected Unauthorized but nothing was raised
    
[/code]

プロダクトコードは以下のようでテストでmrqを実行したときに任意のエラーを発生させようとしてます。

[code]

    def self.find_by_id(conn, id)
      options = {…}
      response = Job.mrq(conn, options)
    
[/code]

テストコードです。

[code]

    before(:each) do
      Job.should_receive(:mrq).and_raise(ex)
    end
    context "…の場合は、" do
      let(:ex){SomeError.new("Unauthorized", 401)}
      it "そのまま例外をraiseする。" do
        lambda{Job.find_by_id(@conn, 0)}.should raise_error(ex)
      end
    end
    
[/code]

rspec (2.11.0)を使用してます。ruby 1.9.2では動作していました。




ドキュメントに書いてあるように、`raise_error`マッチャ―の引数に渡すのは例外クラスです。例外クラスのインスタンスではありません。  
<https://www.relishapp.com/rspec/rspec-expectations/v/2-11/docs/built-in-
matchers/raise-error-matcher>

以下のように、`SomeError`クラスを渡すようにすると通ると思います。

[code]

    lambda{Job.find_by_id(@conn, 0)}.should raise_error(SomeError)
    
[/code]

より詳しく例外オブジェクトについて検証したいのであれば、ブロックを渡して検証するといいです。

[code]

    lambda{Job.find_by_id(@conn, 0)}.should raise_error(SomeError) {|e| e.should eq ex }
    
[/code]

<https://www.relishapp.com/rspec/rspec-expectations/v/2-11/docs/built-in-
matchers/raise-error-matcher#set-expectations-on-error-object-passed-to-block>

## なぜRubyのバージョンを上げて落ちるようになったか

内部の実装的には、`raise_error`マッチャーの引数に渡した値が`rescue`節に渡ります。  
<https://github.com/rspec/rspec-
expectations/blob/v2.11.0/lib/rspec/matchers/built_in/raise_error.rb#L23>

以下のように、`rescue`に例外クラスのインスタンスを渡すようなプログラムが、Ruby 1.9.2では実行出来るが、Ruby
2.2.3では実行できなくなったため、バージョンアップで落ちるようになったのではないでしょうか。

[code]

    class FooError < StandardError; end
    bar = FooError.new
    begin; raise bar; rescue bar => e; p e; end
    
[/code]




ドキュメントに書いてあるように、`raise_error`マッチャ―の引数に渡すのは例外クラスです。例外クラスのインスタンスではありません。  
<https://www.relishapp.com/rspec/rspec-expectations/v/2-11/docs/built-in-
matchers/raise-error-matcher>

以下のように、`SomeError`クラスを渡すようにすると通ると思います。

[code]

    lambda{Job.find_by_id(@conn, 0)}.should raise_error(SomeError)
    
[/code]

より詳しく例外オブジェクトについて検証したいのであれば、ブロックを渡して検証するといいです。

[code]

    lambda{Job.find_by_id(@conn, 0)}.should raise_error(SomeError) {|e| e.should eq ex }
    
[/code]

<https://www.relishapp.com/rspec/rspec-expectations/v/2-11/docs/built-in-
matchers/raise-error-matcher#set-expectations-on-error-object-passed-to-block>

## なぜRubyのバージョンを上げて落ちるようになったか

内部の実装的には、`raise_error`マッチャーの引数に渡した値が`rescue`節に渡ります。  
<https://github.com/rspec/rspec-
expectations/blob/v2.11.0/lib/rspec/matchers/built_in/raise_error.rb#L23>

以下のように、`rescue`に例外クラスのインスタンスを渡すようなプログラムが、Ruby 1.9.2では実行出来るが、Ruby
2.2.3では実行できなくなったため、バージョンアップで落ちるようになったのではないでしょうか。

[code]

    class FooError < StandardError; end
    bar = FooError.new
    begin; raise bar; rescue bar => e; p e; end
    
[/code]

