fbelanger
fbelanger

Reputation: 3568

RSpec Shared Examples - Passing Data to Shared Specs

I've tried multiple ways of getting the data I need within my shared specs, however I always get undefined values.

I am doing something similar to the following:

require 'spec_helper'

describe UserAnalyticsService do

  before(:each) { @user = FactoryGirl(:user) }

  let(:user_query) { UserAnalyticsQuery.build(@user) }
  let(:totals) { UserAnalyticsService.new(user_query) }

  it_should_behave_like "an array of hashes" # What I want
end

I've tried the following:

Nested let()

shared_examples "an array of hashes" do
  it { expect(array).to be_an_instance_of(Array) }
  it "each element should be an instance of Hash" do
    array.each { |element| expect(element).to be_an_instance_of(Hash) }
  end
end

And doing:

using a let()

it_should_behave_like "an array of hashes" do
  let(:array) { totals.inactive_users }
end

using instance variable

before(:each) { @array = totals.inactive_users }

Then

it_should_behave_like "an array of hashes" do
  let(:array) { @array }
end

Block Params

shared_examples "an array of hashes" do |array|
  it { expect(array).to be_an_instance_of(Array) }
  it "each element should be an instance of Hash" do
    array.each { |element| expect(element).to be_an_instance_of(Hash) }
  end
end

Then

it_should_behave_like "an array of hashes", @array

All of the following results in nil pointer exceptions and undefined variables.

Any advice, suggestions or recommendations are welcomed, thanks in advance.

EDIT

Okay, so I've been looking deeper into let() and am realizing that data passed to a shared example has to existing before the transactional block.

I'm pretty sure this was my issue as I was using before(:each) and let() to pass data, however those are both undefined until we reach the example group.

Input is still very much welcomed, especially on alternatives or perspectives to help get these common specs into a shared example.

Upvotes: 3

Views: 1958

Answers (1)

David
David

Reputation: 3610

I must admit I was confused by use of rspec shared_examples and gave up on them the last time I tried to work with them, but your question inspired me to have another look.

Surprisingly it actually turned out to be very straightforward and didn't take too long at all to knock up some tests that passed - I'm either missing something fundamental in your question or the following should give you some hint at what you need to do.

The tests themselves should be self-explanatory:

require 'rails_helper'

RSpec.describe Array, type: :class do
  shared_examples 'an array of hashes' do
    it { expect(array).to be_an_instance_of(Array) }

    it 'each element should be an instance of Hash' do
      array.each { |element| expect(element).to be_an_instance_of(Hash) }
    end
  end

  describe 'with an array of hashes' do
    context 'with predefined array' do
      let(:hash) { Hash.new(name: 'hash', value: 'value') }
      let(:array) { [hash, hash, hash] }

      context 'without using shared examples' do
        it { expect(array).to be_an_instance_of(Array) }

        it 'each element should be an instance of Hash' do
          array.each { |element| expect(element).to be_an_instance_of(Hash) }
        end
      end

      context 'using shared examples' do
        it_should_behave_like 'an array of hashes'
      end
    end

    context 'when passing array to shared example' do
      let(:hash) { Hash.new(name: 'hash', value: 'value') }
      let(:myarray) { [hash, hash, hash] }

      it_should_behave_like 'an array of hashes' do
        let(:array) { myarray }
      end

      context 'with use of before(:each) block' do
        before(:each) do
          @myarray = myarray
        end

        it_should_behave_like 'an array of hashes' do
          let(:array) { @myarray }
        end
      end
    end
  end
end

There should be no reason why the following shouldn't work either:

it_should_behave_like 'an array of hashes' do
  let(:array) { totals.inactive_users }
end

Upvotes: 2

Related Questions