我应该如何在Rails中设置复杂的工厂?

时间:2014-09-24 05:53:02

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

在我的rails应用程序中,我有三个对象,我想为特定的Rspec测试建模。这些是组织,用户和销售机会。

class Organization
  has_many :users
end

class User
  has_many :sales_opportunities
end

组织必须具有唯一的名称,并且要使我的应用程序的授权/身份验证方面起作用,两个用户都需要属于同一个组织。出于此测试的目的,我希望一个用户为admin,另一个为非admin。

这在我的应用程序的其他部分成功运行(例如注册 - 第一个用户自动为管理员,后续用户不是,并且用于查看页面上的链接等)。但是,我现在需要做的是创建两个用户(两个属于一个组织),使其中一个成为管理员,然后当我删除 sales_opportunity 非管理员用户时,任何属于此非的sales_opportunities -admin用户应将所有权转移给管理员(用于重新分配)。

FWIW我正在使用其他一些测试的关联 - 但在这种情况下,我无法创建与一个组织关联的两个用户,所以我已经使用过:

before(:create) do |organization|
    organization.users << FactoryGirl.build(:user ...admin params)
    organization.users << FactoryGirl.build(:user ...non_admin params)
end

在工厂中为组织构建2个用户,这两个用户都属于一个组织并具有不同的特征。这很有效。

如果我尝试在工厂之前添加另一个(:create)然后构建一个sales_opportunity则失败(​​因为用户尚未创建但我相信)。在(:create)之后,陈述也会持平。我也尝试在测试中自己定义non_admin用户(例如,使用let(:non_admin)= Organization.users.last然后使用non_admin.sales_opportunities.create语句),但这无法产生任何销售机会(不确定它是做什么的。)

有没有关于如何构建这样的测试的好资源?我可以编写代码来立即解决问题 - 我似乎浪费了大量的时间来编写测试。我确定这些都不是特别干,要么通过我的工厂。

任何帮助都将不胜感激。

2 个答案:

答案 0 :(得分:1)

rspec / factories / sales_opportunities.rb 中的

FactoryGirl.define do
  sequence(:name) { |n| "Sales Oppotunity - #{n}" }

  factory :sales_opportunity do
    user
    name { generate(:name) }
    # add more attributes here
  end
end
rspec / factories / users.rb 中的

FactoryGirl.define do

  factory :user do
    organization

    sequence(:name) { |n| "Test user #{n}" }
    sequence(:email) { |n| "test.user#{n}@example.com" }
    password "password"

    admin false
    # add more attributes here
  end

  factory :admin_user, parent: :user do
    admin true
  end

  factory :user_with_sales_opportunities, parent: :user, traits: [:with_sales_opportunity]

  factory :admin_user_with_sales_opportunities, parent: :admin_user, traits: [:with_sales_opportunity]

  trait :with_sales_opportunity do
    ignore do # instead of ignore use transient if you're using factory girl 4.3 or above
      sales_opportunities_count 5
    end

    after(:create) do |user, evaluator|
      sales_opportunities { FactoryGirl.create_list(:sales_opportunity, evaluator.sales_opportunities_count, user: user) }
    end
  end
end
rspec / factories / organizations.rb 中的

FactoryGirl.define do
  sequence(:name) { |n| "Organization - #{n}" }

  factory :organization do
    name { generate(:name) }

    ignore do
      users_count 5
      admin_users_count 2
      sales_opportunities_count 5
    end

    after(:create) do |organization, evaluator|
      users { FactoryGirl.create_list(:user_with_sales_opportunities, evaluator.users_count, organization: organization, sales_opportunities_count: sales_opportunities_count) + FactoryGirl.create_list(:admin_user_with_sales_opportunities, evaluator.users_count, organization: organization, sales_opportunities_count: sales_opportunities_count) }
    end
    # add more attributes here
  end
end

现在你可以:

let(:organization) { FactoryGirl.create(:organization, users_count: 4, admin_users_count: 3, sales_opportunities_count: 4) }

答案 1 :(得分:0)

我不确定您要做什么,因为您似乎正在删除要转移的销售机会?

如果您认为它是一个rspec,您可以尝试在(:build)之后使用

类似的东西:

factory :user, class: User do
  sequence(:email) { |n| "user#{n}@example.org" }
  password "foobar"
  # Standard user stuff etc

  trait :admin do
    after(:build) do |user|
      user.set_admin
    end
  end
end

factory :organisation, class: Organisation do
  name "Super Duper"

  after(:build) do |organization|
    organization.users << FactoryGirl.build(:user)
    organization.users << FactoryGirl.build(:user, :admin)
  end
end

我觉得你可能有一个建模问题。您的销售机会不应该是独立的吗?

即:

class SalesOpportunity
  field :name, type: String
  field :phone, type: Integer

  belongs_to :user  
end

class User
  field :name, type: String
  field :phone, type: Integer

  has_many :salesopportunities
end

这样您可以删除用户对象并保留销售机会吗?