When running my specs, I am stopped by a FactoryGirl
error before rspec can even iterate through them.
Finished in 0.18709 seconds (files took 1.57 seconds to load)
0 examples, 0 failures
/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/factory_girl-4.5.0/lib/factory_girl/linter.rb:14:in `lint!': The following factories are invalid: (FactoryGirl::InvalidFactoryError)
* program - Validation failed: Name has already been taken (ActiveRecord::RecordInvalid)
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/factory_girl-4.5.0/lib/factory_girl/linter.rb:4:in `lint!'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/factory_girl-4.5.0/lib/factory_girl.rb:59:in `lint'
from /spec/support/factory_girl.rb:9:in `block (2 levels) in <top (required)>'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/example.rb:333:in `instance_exec'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/example.rb:333:in `instance_exec'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/hooks.rb:357:in `run'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/configuration.rb:1559:in `block in run_hooks_with'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/configuration.rb:1559:in `each'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/configuration.rb:1559:in `run_hooks_with'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/configuration.rb:1525:in `with_suite_hooks'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/runner.rb:109:in `block in run_specs'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/reporter.rb:62:in `report'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/runner.rb:108:in `run_specs'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/runner.rb:86:in `run'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/runner.rb:70:in `run'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/lib/rspec/core/runner.rb:38:in `invoke'
from /.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/rspec-core-3.2.3/exe/rspec:4:in `<top (required)>'
from /.rbenv/versions/2.2.2/bin/rspec:23:in `load'
from /.rbenv/versions/2.2.2/bin/rspec:23:in `<main>'
Without the associated program
in the campaign
factory, my tests run fine with 15 examples, 2 failures
.
Here is my Factory..
FactoryGirl.define do
factory :campaign do |x|
x.sequence(:name) { |y| "Q6 201#{y}" }
x.sequence(:comment) { |y| "JIRA OI-6#{y}" }
channels ["Folder", "Fax"]
program
end
factory :program do
name "Caspian Star"
end
factory :plan do
name "Third Storm Connect"
end
end
My relevant models..
Class Campaign < ActiveRecord::Base
belongs_to :program
validates :program, presence: true
belongs_to :plan
end
Class Program < ActiveRecord::Base
has_many :campaigns
end
It definitely has to do with setting up the associated Program with Campaign but I can't figure out how to do it right. My intention is to not create multiple instances of program
but rather have the campaign associate with an existing one - or the one I create via FactoryGirl.
When using FactoryGirl's association :program
method, I get the same error. It doesn't seem to matter what I name the program
factory to either. I am also using DatabaseCleaner
to clear the test database after it runs.
I'm currently trying to test the validates :program, presence true
but keep running in circles with this.
Any help is highly appreciated.
UPDATE
Here are the specs as requested.
describe "Relationships" do
it { should belong_to :program }
...some unrelated relationship specs..
end
describe "Validations" do
it { should validate_presence_of :name }
it { should validate_uniqueness_of :name }
it { should serialize :channels }
it { should validate_presence_of :comment }
it { should validate_presence_of :program }
end
it "serializes column into Array" do
obj = build(:campaign)
obj.channels = [1,2,3]
obj.save!
expect(obj.reload.channels).to eq [1, 2, 3]
end
it 'validates associated campaign' do
campaign = build(:campaign)
expect(campaign.save).to be_valid?
expect(campaign.errors).to eq "You need to choose a Program."
end
end
UPDATE #2
After experimenting with some of the answers below, I can confirm that sequence
does not fix the error. However, when I completely remove the FactoryGirl association yet instantiate the association in the specs - I recieve the error Validation failed: Program can't be blank
. Note, my specs are still not running..
Finished in 0.19413 seconds (files took 1.54 seconds to load)
0 examples, 0 failures
Something is going on before it hits my specs and I believe the answer is what the error refers to lint!
I'm not too familiar with 'lint', as I set my Factory up following a blog. I checked the documentation and it appears that I have it set up correctly...however, it runs my Factories through my validations before the database is cleaned as well as before it runs any specs.
This is a problem when I want to validates :program, presence: true
and instantiate it in the spec. 'FactoryGirl.lint' interrupts my test letting me know it can't be blank
when that is exactly what I want the Factory to look like. So far, the only solution I can think of is to disable lint
as I was able get my specs to run with it disabled.. but now that I understand it more, I can see how it could become very useful.
Is there a win-win situation for this scenario? Can I still have lint
while preserving validations and associations the way I need them to be?
Here's a look at my spec/support/factory_girl.rb where lint
exists..
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.before(:suite) do
begin
DatabaseCleaner[:active_record].strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
DatabaseCleaner.start
FactoryGirl.lint
ensure
DatabaseCleaner.clean
end
end
end
Does Program
validate uniqueness of it's name
attribute? If so, you'll need to redefine the program
factory to generate a unique name for each instance it generates.
factory :program do
sequence(:name) { |n| "Caspian Star #{n}" }
end
Or if you have a particular program you've seeded (or otherwise guaranteed to exist) and a way to access it, you could use a block when declaring the association
factory :campaign do |x|
...
program { Program.some_program }
end
You're probably instantiating two program
s and a program's name in your factory is hard-coded. Make it a sequence (like you did for campaigns) and you will be good to go.
* Update * If you want your campaigns to have the same Program, you have to remove the program assignment from within the Campaign's factory and pass it explicitly in your specs. Like this:
FactoryGirl.define do
factory :campaign do |x|
x.sequence(:name) { |y| "Q6 201#{y}" }
x.sequence(:comment) { |y| "JIRA OI-6#{y}" }
channels ["Folder", "Fax"]
# no more program here
end
factory :program do
name "Caspian Star"
end
factory :plan do
name "Third Storm Connect"
end
end
And your specs:
let(:program) { FactoryGirl.create(:program) }
let(:campaign) { FactoryGirl.build(:campaign, program: program) }
describe "Relationships" do
it { campaign.should belong_to :program }
...some unrelated relationship specs..
end
describe "Validations" do
it { campaign.should validate_presence_of :name }
it { campaign.should validate_uniqueness_of :name }
it { campaign.should serialize :channels }
it { campaign.should validate_presence_of :comment }
it { campaign.should validate_presence_of :program }
end
Alternatively, the code above allows you to leave the factory as it is and overwrite the program instance as needed.
In your specs, you're building objects with build
+ save!
, while you should probably use FactoryGirl.create
:
it "serializes column into Array" do
campaign = FactoryGirl.create(:campaign, channels: [1,2,3])
expect(campaign.channels).to eq [1, 2, 3]
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With