RSpec 3.4ではエラーが起きた行を出力結果に表示してくれるようになった


## はじめに

2015年11月にRSpec 3.4がリリースされました。
派手は新機能はありませんが、実践的な機能改善が施され、テストのサイクルを効率よく回せるようになっています。

主な新機能についてはこちらの公式日本語訳（！）をご覧ください。

<blockquote class="twitter-tweet" lang="en"><p lang="ja" dir="ltr">今回のリリースから日本語訳を書くことにしました :) / RSpec 3.4 がリリースされました！ <a href="https://t.co/HpJkYyEMV7">https://t.co/HpJkYyEMV7</a></p>&mdash; Yuji Nakayama (@nkym37) <a href="https://twitter.com/nkym37/status/665379622758936577">November 14, 2015</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

公式ブログには以下のような新機能が紹介されています。

- Core: Bisect アルゴリズムの改善
- Core: 失敗時の出力の改善
  - 複数行のコードスニペット
  - coderayがインストール済みの場合、シンタックスハイライトが有効に
  - 失敗元の行の検出の改善
- Expectations: 複合エクスペクテーションの失敗時メッセージの改善
- Expectations: match マッチャへの with_captures の追加
- Rails: ActiveJob のための新しい have_enqueued_job マッチャ

この中で僕が一番注目したのが「 **失敗元の行の検出の改善** 」です。
ただ、残念なことにブログにはコードや出力結果の例が載っていなかったので、自分で実際に動かして確認してみました。

この記事ではその内容を紹介します。

## テストコード

以下はContactクラスのnameメソッドをテストするテストコードです。
nameメソッドはfirstnameとlastnameを半角スペースで連結します。

```spec/models/contact_spec.rb
describe Contact do
  # ...
  
  it "returns a contact's full name as a string" do
    contact = build_stubbed(:contact,
      firstname: 'Jane',
      lastname: 'Smith'
    )
    expect(contact.name).to eq 'Jane Smith'
  end
  
  # ...
end
```

## Contactクラスの実装

以下はContactクラスの実装です。
ただし、テストが失敗するようにわざと join の引数を半角スペースではなく 1 に変えています。

```app/models/contact.rb
class Contact < ActiveRecord::Base
  # ...
  
  def name
    # わざと join の引数を 1 に変えた
    [firstname, lastname].join(1)
  end
  
  # ...
end
```

## RSpec 3.3での実行結果

RSpec 3.3では "Failure/Error:" にテストが失敗したエクスペクテーション（`expect(...).to ...`）が表示されています。

```text
Failures:

  1) Contact returns a contact's full name as a string
     Failure/Error: expect(contact.name).to eq 'Jane Smith'
     TypeError:
       no implicit conversion of Fixnum into String
     # ./app/models/contact.rb:11:in `join'
     # ./app/models/contact.rb:11:in `name'
     # ./spec/models/contact_spec.rb:18:in `block (2 levels) in <top (required)>'
     # ./spec/rails_helper.rb:40:in `block (3 levels) in <top (required)>'
     # ./spec/rails_helper.rb:39:in `block (2 levels) in <top (required)>'
```

![Screen Shot 2015-11-15 at 15.21.30.png](https://qiita-image-store.s3.amazonaws.com/0/7465/07958072-27dd-0a3f-8add-bfa063f63c85.png "Screen Shot 2015-11-15 at 15.21.30.png")


## RSpec 3.4での実行結果

RSpec 3.4では "Failure/Error:" にエラーが発生した行（Modelのコード）が表示されています。

```text
Failures:

  1) Contact returns a contact's full name as a string
     Failure/Error: [firstname, lastname].join(1)
     
     TypeError:
       no implicit conversion of Fixnum into String
     # ./app/models/contact.rb:11:in `join'
     # ./app/models/contact.rb:11:in `name'
     # ./spec/models/contact_spec.rb:18:in `block (2 levels) in <top (required)>'
     # ./spec/rails_helper.rb:40:in `block (3 levels) in <top (required)>'
     # ./spec/rails_helper.rb:39:in `block (2 levels) in <top (required)>'
```

![Screen Shot 2015-11-15 at 15.21.42.png](https://qiita-image-store.s3.amazonaws.com/0/7465/83878cd7-4a17-291f-1cc0-e9a022a4dafd.png "Screen Shot 2015-11-15 at 15.21.42.png")

## RSpec 3.4で失敗行を表示するルール

RSpec 3.3ではいつでもspecファイル（xxx_spec.rb）内で失敗した行を "Failure/Error:" として表示しています。

RSpec 3.4では `config.project_source_dirs` で定義されたディレクトリ内にあるコードが出力対象になります。
`config.project_source_dirs` のデフォルト値は `['spec', 'lib', 'app']` です。
上のサンプルコードでは `app/models/contact.rb` 内でエラーが起きたので、 "Failure/Error:" にModelのコードが表示されています。

Ruby本体やgemのコードは `config.project_source_dirs` に含まれないので、 "Failure/Error:" に表示されません。

## まとめ

上のコードで示したように、RSpec 3.4ではRSpec 3.3に比べてエラーの発生箇所に近い場所を表示してくれるので、エラーの原因を特定しやすくなりました。
便利ですね！！

### 参考文献

- [RSpec 3.4 がリリースされました！](http://rspec.info/ja/blog/2015/11/rspec-3-4-has-been-released/)
- [Improve logic that finds the failure line to print. · rspec/rspec-core@aadd339](https://github.com/rspec/rspec-core/commit/aadd339c9ebdbffcfad52b0a94c435b787ffdd66)

### RSpec 3.3についてはこちら

RSpec 3.3で導入された新機能についてはこちらにまとめてあります。

[実用的な新機能が盛りだくさん！RSpec 3.3 完全ガイド - Qiita](http://qiita.com/jnchito/items/3a590480ee291a70027c)

「RSpec 3.4で改善されたBisectアルゴリズムって何？」と思ったりした人は上の記事を読んでみてください。

### サンプルコードはEveryday Railsで使用しているコードです

サンプルコードは電子書籍「 **Everyday Rails - RSpecによるRailsテスト入門** 」で使用しているコードです。
RSpec初心者の方に最適な一冊ですので、よかったら読んでみてください。

- [Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)