Rspec: Validation failed: Name has already been taken
Asked Answered
S

5

10

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
Sensitivity answered 18/6, 2015 at 23:21 Comment(1)
Can you add also the code of the test itself?Filiation
F
8

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
Fencer answered 18/6, 2015 at 23:31 Comment(1)
Thank you for the response. It does validate_uniqueness but is there a way to make the Program static and the Campaign object associate with it? I will clarify my post above to make that intention more apparent.Sensitivity
F
3

You're probably instantiating two programs 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.

  • Bonus *

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
Filiation answered 19/6, 2015 at 0:26 Comment(5)
I commented out the specs and I still get the error though. I'd like to keep the program object static if possible. A program has_many :campaigns so when I create a campaign, I just need to associate it with one.Sensitivity
The problem is that your campaign factory, every time that gets called, asks implicitly for a new program, hence the error. Let me update my answer.Filiation
Thank you for the update with the added bonus! Unfortunately I still get an error Validation failed: Program can't be blank, which I've seen previous to the error stated as the title of this posting. I'll update my post with what I now believe is the root cause..Sensitivity
Sorry for catching up only now. If you change the campaign's factory not to have a program, all the tests that don't explicitly assign one will fail with the blank error. Is that the case?Filiation
It's all good. I took a weekend break myself :) If I change the campaign's factory to not have a program, then my tests don't even run. The Validation failed messages interfere with rspec, keeping it from running any specs. I get the 0 examples, 0 failures.Sensitivity
I
3

I had this problem because I had created a record with the same name before so I had to run:

rails db:setup

And then I got rid of it and it started working again.

Implead answered 20/2, 2017 at 15:9 Comment(0)
C
1

I'm not sure if this is the same issue that you're having, but I was getting a similar error and the issue was that I had a Model.create! method call in the spec (which fails with a validation error message instead of failing silently). As soon as I removed the bang ! everything ran properly.

Conspecific answered 2/2, 2016 at 0:56 Comment(1)
create (without !) saves the object to the DB if it passed validation, and returns it in any case. So removing it, your code will appear to work but won't write to the DB — probably not what you want.Eccles
S
1

I dont know if this problem is solved or not.. considering it is a pretty old question. I struggled with this issue a lot myself. Adding my answer just for documentation.

I used sequence in my factory for ModelB, That did not help with the error. I also tried database_cleaner. Still no success.

The reason for the error is explained by Joshua Clayton in this post https://github.com/thoughtbot/factory_girl/issues/523

What I understood is that even when I used sequence, I was creating my ModelB somewhere in my tests previous to using the factory. Factorygirl was not able to keep track of the records with the sequence. Hence the name was already taken.

I suspect the following code was causing it all.

class ModelA
  before_create :create_model_b
...

As a quick and dirty way to solve the problem, I changed the factory for ModelB to offset the sequence by a large number.

FactoryGirl.define do
  factory :model_b do
    sequence(:name){|n| "name_#{n+1000}"} 
    ...

  end
end

It worked !! Seems to be pretty dumb a solution, but my specs are all green now :)

Sullins answered 7/2, 2017 at 7:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.