Rspec:验证失败:名称已被取消

时间:2015-06-18 23:21:58

标签: ruby-on-rails ruby rspec tdd factory-bot

在运行我的规范时,我在FactoryGirl错误之前停止了,之后rspec甚至可以遍历它们。

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>'

如果program工厂中没有关联的campaign,我的测试就可以15 examples, 2 failures运行。

这是我的工厂..

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

我的相关模型..

Class Campaign < ActiveRecord::Base
    belongs_to :program
    validates :program, presence: true

    belongs_to :plan
end

Class Program < ActiveRecord::Base
    has_many :campaigns
end

这肯定与使用Campaign设置相关程序有关,但我无法弄清楚如何正确执行。我的目的是不创建program的多个实例,而是让广告系列与现有广告相关联 - 或者我通过FactoryGirl创建的广告系列。

使用FactoryGirl的association :program方法时,我收到同样的错误。我对program工厂的名称似乎并不重要。我在运行后也使用DatabaseCleaner清除测试数据库。

我目前正在尝试对validates :program, presence true进行测试,但会继续在圈子中运行。

非常感谢任何帮助。

更新

以下是所要求的规格。

    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

更新#2

在尝试下面的一些答案后,我可以确认sequence无法解决错误。但是,当我完全删除FactoryGirl关联但实例化规范中的关联时 - 我收到错误Validation failed: Program can't be blank。请注意,我的规格仍未运行..

Finished in 0.19413 seconds (files took 1.54 seconds to load)
0 examples, 0 failures

在达到我的规格之前还有一些事情发生了,我相信答案就是错误所指的lint!我不太熟悉&#39; lint&#39;,因为我设置了我的工厂跟随博客。我检查了文档,似乎我已正确设置...但是,它在数据库清理之前以及运行任何规范之前通过我的验证运行我的工厂。

当我想validates :program, presence: true并在规范中实例化时,这是一个问题。 &#39; FactoryGirl.lint&#39;中断了我的测试,让我知道它can't be blank,这正是我想要工厂看起来的样子。到目前为止,我能想到的唯一解决方案是禁用lint ,因为我可以让我的规格在禁用的情况下运行..但现在我了解更多,我可以看到它如何变得非常有用。

这种情况是否存在双赢局面?我可以按照lint的方式保留验证和关联吗?

在这里查看我的 spec / support / factory_girl.rb ,其中lint存在..

 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

5 个答案:

答案 0 :(得分:4)

Program是否验证了name属性的唯一性?如果是这样,您需要重新定义program工厂,为其生成的每个实例生成唯一的名称。

factory :program do
  sequence(:name) { |n| "Caspian Star #{n}" }
end

或者,如果你有一个特定的程序,你已经播种(或保证存在)以及一种访问它的方法,你可以在声明关联时使用一个块

factory :campaign do |x|
  ...
  program { Program.some_program }
end

答案 1 :(得分:3)

您可能正在实例化两个program,并且您工厂中的程序名称是硬编码的。制作一个序列(就像你为广告系列所做的那样),你会很高兴。

*更新* 如果您希望广告系列具有相同的程序,则必须从Campaign的工厂中删除程序分配,并在规范中明确传递。像这样:

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

你的规格:

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

或者,上面的代码允许您按原样离开工厂并根据需要覆盖程序实例。

  • 奖金*

在您的规范中,您使用build + save!构建对象,而您应该使用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

答案 2 :(得分:1)

我不确定这是否与您遇到的问题相同,但我遇到了类似的错误,问题是我在规范中调用了Model.create!方法(失败并显示验证错误消息而不是静默失败)。一旦我删除了爆炸!,一切都正常运行。

答案 3 :(得分:1)

我遇到了这个问题,因为我之前创建了一个具有相同名称的记录,因此我必须运行:

rails db:setup

然后我摆脱它,它又开始工作了。

答案 4 :(得分:0)

我不知道这个问题是否已经解决。考虑到这是一个非常古老的问题。我自己也很努力地解决这个问题。添加我的答案只是为了文档。

我在我的工厂中为ModelB使用了序列,这对错误没有帮助。我也尝试过database_cleaner。仍然没有成功。

Joshua Clayton在这篇文章中解释了错误的原因 https://github.com/thoughtbot/factory_girl/issues/523

我所理解的是,即使我使用了序列,我在使用工厂之前的测试中创建了我的ModelB。 Factorygirl无法跟踪序列中的记录。因此这个名字已经被采用了。

我怀疑以下代码导致了这一切。

class ModelA
  before_create :create_model_b
...

作为解决问题的快速而肮脏的方法,我更改了ModelB的工厂以使序列偏移大量。

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

  end
end

有效!! 似乎是一个相当愚蠢的解决方案,但我的规格现在都是绿色的:)