Question

I want to loop over an array of objects that are built by factory_girl outside of an it block.

Example:

before :each do
  @array = build(:super_cool_array_of_objects)
end

context 'I want access to @array here' do

  @array.each do |item|
    it 'tests item for some interesting things' do
      item.should be_true # my test is more complicated than this
    end
  end

end

But when I do this, I get an error that @array is nil since the before :each hasn't been evaluated yet. I've tried setting @array in a let and let! but haven't had any success.

Do you have any other suggestions?

Was it helpful?

Solution

I expect that what you want to do is isolate the logic of building something using FactoryGirl in a method and then using that to define various Rspec examples.

Here's a technique of doing so by creating a global method.

def how_about_a_method
  2.times.map { FactoryGirl.build(:foo) }
end

describe 'lucys question' do
  context 'accessing something' do
    how_about_a_method.each do |item|
      it 'should have an item' do
        expect(item).to be
      end
    end
  end
end

Note that you'll need to use FactoryGirl.build because in your spec_helper you've specified to include the FactoryGirl syntax methods with config.include FactoryGirl::Syntax::Methods but in this global method you're not in an RSpec class anymore so FactoryGirl hasn't been included. I tend to prefer to not include FactoryGirl as it specifies in the documentation, but will instead always reference FactoryGirl explicitly as I have done in the method here.

A more interesting question is "Why is @array nil?". It helps to understand a little bit about how RSpec works. Inside your it block is what is known as an example. This is evaluated in a different scope to the context block (which is known as an ExampleGroup). Attempting to use result of before, let or let! in the ExampleGroup context doesn't work because they are also evaluated in the Example context, that is the same scope your it block is evaluated in.

Obviously a global method is global so you can access it wherever you like.

Personally I think the scoping is the most complicated part of RSpec. I wish it were documented better.

OTHER TIPS

This works for me.

describe 'something' do
  array = [1,2,3]
  context 'access array here' do
    array.each do |item|
      it 'tests item for some interesting things' do
        item.should be_true # my test is more complicated than this
      end
    end
  end
end

FactoryGirl version:

You can drop FactoryGirl.build(...) as a replacement expression to [1,2,3].

gem 'factory_girl'
require 'factory_girl'

FactoryGirl.define do
  factory :super_cool_array_of_objects, class: Array do
    initialize_with { [1,2,3] }
  end
end

describe 'something' do
  context 'access array here' do
    FactoryGirl.build(:super_cool_array_of_objects).each do |item|
      it "tests item #{item} for some interesting things" do
        item.should be_true # my test is more complicated than this
      end
    end
  end
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top