include_examplesとit_behaves_likeの違い【RSpec】

# 要約

* `include_context` と `include_examples` はエイリアスの関係。動作は変わらない。
* `include_(context|examples)` は、現在のコンテキストに直接テストケースを埋め込む。
* `it_behaves_like` は、新しいコンテキストを自動生成して、そこにテストケースを埋め込む。
* **基本的に it_behaves_like を使った方が良い。**


# include_context の動作

include_context は、 **現在のコンテキストに直接テストケースを埋め込みます。**

……え、意味が分からない？ 大丈夫、私もドキュメントの "include the examples in the current context" という解説を初めて見たときは理解できませんでした。

言葉では説明しづらいので、コードで説明します。

例えば、以下のようにコードを記述したとき

```ruby
# 共有したい example を定義
shared_examples 'test1' do
  it 'something が 1 であること' do
    expect(something).to eq 1
  end
end

# テストを記述
describe 'hoge' do
  let(:something) { 1 }

  include_context 'test1'
end
```

実行時には以下のように解釈されます。

```ruby
describe 'hoge' do
  let(:something) { 1 }

  # include_context 'test1' の部分に直接埋め込まれる
  it 'something が 1 であること' do
    expect(something).to eq 1
  end
end
```


# include_examples の動作

include_examples は、 **include_context のエイリアス(別名)です。**

つまり、 include_examples の動作は include_context と全く同じです。
どうして同じ機能が別々の名前で用意されているのかというと、 rspec は自然言語(人間が普段つかう言葉)に近い文章でテストが書けることを目指して作られているからです。
「人間が読んだ時に、より理解しやすい方を選んでね」って意図で、複数の名前が定義されているんですね。
rspec は他にも、違う名前で同じ振る舞いをするものが沢山あります。


# it_behaves_like の動作

it_behaves_like は、 **新しいコンテキストを自動生成して、そこにテストケースを埋め込みます。**

これもコードで見た方が分かりやすいです。
以下のようにコードを記述すると、

```ruby
# 共有したい example を定義
shared_examples 'test1' do
  it 'something が 1 であること' do
    expect(something).to eq 1
  end
end

# テストを記述
describe 'hoge' do
  let(:something) { 1 }

  it_behaves_like 'test1'
end
```

実行時には以下のように解釈されます。

```ruby
describe 'hoge' do
  let(:something) { 1 }

  # it_behaves_like 'test1' の部分に、自動で新しい context が生成される
  context 'behaves like a test1' do
    it 'something が 1 であること' do
      expect(something).to eq 1
    end
  end
end
```

# include_(context|examples) の罠

一見、直接埋め込もうが、自動でコンテキストを挿入しようが、たいした違いはなさそうに思えます。
しかし、実は include_(context|examples) には、「定義の上書き」と呼ばれる(というか僕が勝手にそう呼んでいる)罠があるのです。

以下のようなテストコードを考えてみましょう。

```ruby
shared_examples 'test1' do
  let(:something) { 1 }
  
  it 'something が 1 であること' do
    expect(something).to eq 1
  end
end

shared_examples 'test2' do
  let(:something) { 2 }
  
  it 'something が 2 であること' do
    expect(something).to eq 2
  end
end

describe 'hoge' do
  include_context 'test1'
  include_context 'test2'
end
```

上記のテストは、実行時には以下のように解釈されます。

```ruby
describe 'hoge' do
  let(:something) { 1 }

  it 'something が 1 であること' do
    expect(something).to eq 1
  end

  let(:something) { 2 }

  it 'something が 2 であること' do
    expect(something).to eq 2
  end
end
```

同じコンテキストに２つの let が埋め込まれることで、片方の定義が上書きされてしまい、「something が 1 であること」のテストが失敗します。
include_(context|example) をメソッド感覚で使うと、上記のようなミスを犯してしまいがちです。

一方、 it_behaves_like ならば、

```ruby
describe 'hoge' do
  context 'behaves like a test1' do
    let(:something) { 1 }

    it 'something が 1 であること' do
      expect(something).to eq 1
    end
  end

  context 'behaves like a test2' do
    let(:something) { 2 }

    it 'something が 2 であること' do
      expect(something).to eq 2
    end
  end
end
```

のように context が分離されるため、安全です。

# 結局、どう使い分ければいいの？

include_(context|examples) には上述したような罠があるので、 **常に it_behaves_like を使えばいいと思います。** 
「include_(context|examples) じゃないと困る」という場面はありません。（断言）


# 参考資料

https://rspec.info/features/3-13/rspec-core/example-groups/shared-examples/

https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/example_group.rb