かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

RSpec include_context って何

プロジェクトの RSpec をみていて、 include_context 〜 という文章があり共通処理をしているっぽいけど、どういうものなのか知らなかったので調べたメモ

include_context

ref. shared context - Example groups - RSpec Core - RSpec - Relish
shared_context 〜 で定義された共通処理を include する

e.g.

# shared/login.rb 共通処理 
RSpec.shared_context 'login as user' do
  let(:login_user) { create :user }
  before { sign_in_as login_user }
end

# page_spec.rb 共通処理を使うテスト
RSpec.describe 'Page' do
  # 共通処理 login as user を読み込み
  include_context 'login as user'
  
  describe '...' do
    # ...
  end
end

page_spec.rb のテスト実行時には次のように解釈される

RSpec.describe 'Page' do
  # include_context している内容がここに入り実行されるということらしい。
  let(:login_user) { create :user }
  before { sign_in_as login_user }
  
  describe '...' do
    # ...
  end
end

shared_context で定義する共通処理の名前は文字列でスペースを含んでもOKっぽい。

shared_contextshared_examplesinclude_contextinclude_examples としてもいいらしい。(エイリアスになってるらしいけど、ドキュメントが別ページになってるのでチョットよく理解できてない。
cf. shared examples - Example groups - RSpec Core - RSpec - Relish

include_context の注意点

スコープ?を作らずに処理がそのまま include されるので、let など同じ名前の定義があると上書きされてしまう。

RSpec.shared-examples "sample 1" do
  let(:name) { '星宮いちご' }
  
  it '名前が"星宮いちご"であること' do
    expect(name).to eq('星宮いちご')
  end
end

RSpec.shared-examples "sample 2" do
  let(:name) { 'ジョニー別府' }

  it '名前が"ジョニー別府"であること' do
    expect(name).to eq('ジョニー別府')
  end
end

RSpec.describe 'Sample' do
  include_examples 'sample 1'
  include_examples 'sample 2'
end

↓ 次のように解釈されるので、name が上書きされてしまい、sample 1 の 名前が"星宮いちご"であること のテストが落ちる

RSpec.describe 'Sample' do
  let(:name) { '星宮いちご' }
  
  it '名前が"星宮いちご"であること' do
    expect(name).to eq('星宮いちご')
  end

  let(:name) { 'ジョニー別府' }

  it '名前が"ジョニー別府"であること' do
    expect(name).to eq('ジョニー別府')
  end
end

it_behaves_like

it_behaves_like を使うとcontextを作りネストして include されるので、定義の上書きを回避できる

RSpec.describe 'Sample' do
  it_behaves_like 'sample 1'
  it_behaves_like 'sample 2'
end

RSpec.describe 'Sample' do
  context 'it_behaves_like sample 1' do
    let(:name) { '星宮いちご' }
  
    it '名前が"星宮いちご"であること' do
      expect(name).to eq('星宮いちご')
    end
  end

  context 'it_behaves_like sample 2' do
    let(:name) { 'ジョニー別府' }

    it '名前が"ジョニー別府"であること' do
      expect(name).to eq('ジョニー別府')
    end
  end
end

 
docker 上で Rails 動かしてるけど、テストコマンド走らせるのに超時間が掛かるから、細かい所は実際に試してません。


[参考]

現場で使える Ruby on Rails 5速習実践ガイド

現場で使える Ruby on Rails 5速習実践ガイド