Rspecでテストコードを書くとやたらめったらコードが長くなることがありますよね、ここではShared contextを利用してコードを共有する(DRY)方法を紹介します。
※RSpecのバージョンはRspec3を想定しています。

例えばこんなテストコードがあるとします。


describe 'Unshared' do
  
  context 'in context 1' do
    let(:b){ true }
    let(:i){ 1 }
    let(:s){ 'hello' }

    it "is true" do
      expect(b).to eq(true)
    end
    it "is 1" do
      expect(i).to eq(1)
    end
    it "is hello" do
      expect(s).to eq('hello')
    end
  end

  context 'in context 2' do
    let(:b){ true }
    let(:i){ 1 }
    let(:s){ 'hello' }

    it "is true" do
      expect(b).to eq(true)
    end
    it "is 1" do
      expect(i).to eq(1)
    end
    it "is hello" do
      expect(s).to eq('hello')
    end
  end
end

例えばlet…の箇所を共有化する場合、shared_contextとinclude_contextを使用してlet…箇所を切り出すことが可能です。
以下のようになります。


shared_context "share me context" do
  let(:b){ true }
  let(:i){ 1 }
  let(:s){ 'hello' }
end

describe 'Shared context' do

  context 'in context 1' do
    include_context "share me context"

    it "is true" do
      expect(b).to eq(true)
    end
    it "is 1" do
      expect(i).to eq(1)
    end
    it "is hello" do
      expect(s).to eq('hello')
    end
  end

  context 'in context 2' do
    include_context "share me context"

    it "is true" do
      expect(b).to eq(true)
    end
    it "is 1" do
      expect(i).to eq(1)
    end
    it "is hello" do
      expect(s).to eq('hello')
    end
  end
end

前出のShared Exampleと組み合わせると、以下のようになりますね


shared_context "share me context" do
  let(:b){ true }
  let(:i){ 1 }
  let(:s){ 'hello' }
end

describe 'Shared context and example' do

  shared_examples "share me example" do
    it "is true" do
      expect(b).to eq(true)
    end
    it "is 1" do
      expect(i).to eq(1)
    end
    it "is hello" do
      expect(s).to eq('hello')
    end
  end

  context 'in context 1' do
    include_context "share me context"

    it_behaves_like "share me example"
  end

  context 'in context 2' do
    include_context "share me context"

    it_behaves_like "share me example"
  end
end

Shared Example同様引数も渡せるようです。


shared_context "share me context" do |arg_b, arg_i, arg_s|
  let(:b){ arg_b }
  let(:i){ arg_i }
  let(:s){ arg_s }
end

describe 'Shared context and example args' do

  shared_examples "share me example" do
    it "is true" do
      expect(b).to eq(true)
    end
    it "is 1" do
      expect(i).to eq(1)
    end
    it "is hello" do
      expect(s).to eq('hello')
    end
  end

  context 'in context 1' do
    include_context "share me context", true, 1, 'hello'

    it_behaves_like "share me example"
  end

  context 'in context 2' do
    include_context "share me context", true, 1, 'hello'

    it_behaves_like "share me example"
  end
end