fbelanger
fbelanger

Reputation: 3568

RSpec with FactoryGirl explicit subject

I'm using RSpec with FactoryGirl within a Ruby on Rails environment for testing.

I want to specify my factories as follows:

  factory :user do
    role # stub

    factory :resident do
      association :role, factory: :resident_role
    end

    factory :admin do
      association :role, factory: :admin_role
    end
  end

And I'd like to do something like this in my spec:

require 'rails_helper'

RSpec.describe User, type: :model do
  context "all users" do
    # describe a user
    #   subject { build(:user) }
    #   it { is_expected.to be_something_or_do_something }
  end

  context "residents" do
    # describe a resident
    #   subject { build(:resident) }
    #   it { is_expected.to be_something_or_do_something }
  end

  context "admins" do
    # describe a admin
    #   subject { build(:admin) }
    #   it { is_expected.to be_something_or_do_something }
  end

end

Can this be done by explicitly setting the subject? When I do, I keep getting duplicate roles errors.

If anyone has any advice or suggestion, it would be greatly appreciated!

Upvotes: 3

Views: 1699

Answers (3)

fbelanger
fbelanger

Reputation: 3568

I found the solution to my problems here: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations

What I needed to use in my user factories was association :role, factory: :role, strategy: :build

Upvotes: 0

AmitA
AmitA

Reputation: 3245

But this causes the user_spec.rb to use the :user factory.

No, it does not. Assuming you configured FactoryGirl correctly, RSpec can use whatever factory you'd like "on demand" in any test file. Configuration-wise, in rails_helper.rb throw this in:

RSpec.configure do |config|
   # ...
   config.include FactoryGirl::Syntax::Methods
   # ...
end

Then, in your spec file:

require 'rails_helper'

RSpec.describe User, type: :model do
  context "all users" do
    let(:user) { create(:user) } 

    it 'is a user' do 
      # Here `user` is going to be a user factory
      expect(user.unit).not_to be_present
    end

  end

  context "residents" do
    let(:user) { create(:resident) } 

    it 'is a resident' do 
      # Here `user` is going to be a resident factory
      expect(user.unit).to be_present
    end
  end

  context "admins" do
    let(:user) { create(:admin) } 

    it 'is an admin' do 
      # Here `user` is going to be an admin factory
      expect(user.role).to be('admin_role')
    end
  end

end

In short, you can use create(<factory_name>) on any factory definition that exists in any one of these paths:

test/factories.rb
spec/factories.rb
test/factories/*.rb
spec/factories/*.rb

Note that if you haven't placed the config.include FactoryGirl::Syntax::Methods inside your RSpec.configure, you can still create any factory, by doing FactoryGirl.create(<factory_name>) instead of create(<factory_name>).

Upvotes: 3

Alex Bezek
Alex Bezek

Reputation: 487

I don't think you would want to stop them from auto loading, and I'm not actually sure what your use case is for not allowing them to load?

RSpec automagically fetches the factory for a spec Rspec loads all the factories into memory when your spec helper loads I believe. Because your using factory inheritence your just loading each of these into memory before your tests run, nothing is being called, no objects are being created or built. They are just ready to use in your tests.

Are you getting a specific error or is there some case I'm not seeing that you need?

Upvotes: 0

Related Questions