应该与class_name,foreign_key和primary_key一起使用

时间:2016-05-04 18:37:12

标签: ruby-on-rails rspec shoulda

如何使用ShouldaMatchers测试此ActiveRecord关系?

模型

class ViolatorUnitHistory < ActiveRecord::Base
...
  belongs_to :primary_source, class_name: Source, primary_key: :source_1_id, foreign_key: :id
  belongs_to :secondary_source, class_name: Source, primary_key: :source_2_id, foreign_key: :id
  belongs_to :tertiary_source, class_name: Source, primary_key: :source_3_id, foreign_key: :id
...
end  

class Source < ActiveRecord::Base
  has_many :violator_unit_histories
end

测试

describe "relationships" do
  # Can't figure out this relationship
  it { is_expected.to have_many(:violator_unit_histories).class_name('Source').with_primary_key('source_1_id').with_foreign_key('id') }
end

当前结果

Failures:
  1) Source relationships should have many violator_unit_histories class_name => Source
     Failure/Error: it { is_expected.to have_many(:violator_unit_histories).class_name('Source').with_primary_key('source_1_id').with_foreign_key('id') }
       Expected Source to have a has_many association called violator_unit_histories ()
     # ./spec/models/source_spec.rb:17:in `block (3 levels) in <top (required)>'

以前的结果

it { is_expected.to have_many(:violator_unit_histories) }

Failures:
  1) Source relationships should have many violator_unit_histories
     Failure/Error: it { is_expected.to have_many(:violator_unit_histories) }
       Expected Source to have a has_many association called violator_unit_histories (ViolatorUnitHistory does not have a source_id foreign key.)

我看到测试的belongs_to方面发布了here的答案。但我似乎无法弄清has_many这些更复杂的测试。

2 个答案:

答案 0 :(得分:1)

首先,看起来您的主要/外键关系有点偏离。

从您的模型定义开始:

class ViolatorUnitHistory < ActiveRecord::Base
...
  belongs_to :primary_source, class_name: Source, primary_key: :source_1_id, foreign_key: :id
  belongs_to :secondary_source, class_name: Source, primary_key: :source_2_id, foreign_key: :id
  belongs_to :tertiary_source, class_name: Source, primary_key: :source_3_id, foreign_key: :id
...
end  

class Source < ActiveRecord::Base
  has_many :violator_unit_histories
end

这里的问题是:id似乎不是外键,而是主键。相反,source_1_id似乎是ViolatorUnitHistory模型上定义的外键。这是对的吗?

换句话说,假设您有Source的关联violator_unit_histories的实例,您希望@source.violator_unit_histories返回什么?您的代码中没有任何内容可以清楚地定义violator_unit_histories实际上是什么。

这就是您收到此错误的原因:

  

ViolatorUnitHistory没有source_id外键。

Rails正在source_id类中查找ViolatorUnitHistory外键,但是,其中一个不存在。

此外,在您的测试中,您没有明确定义测试subject的内容。请考虑使用expect(<object>).to语法使事情更加清晰。

答案 1 :(得分:1)

捎带Anthony E所说的,ViolatorUnitHistory =&gt;之间的关系。来源是明确定义的,但反之则不然。您可以说Violator.first.primary_source,但由于数据库的规范化方式,Source.first.violator_unit_histories不正确。

我可能会以与ViolatorUnitHistory =&gt;类似的方式定义每个反向关系。自ViolatorUnitHistory以来的源关系使用多个字段来存储Source.id

您可以添加以下内容:

class Source < ActiveRecord::Base
...
  # I don't think primary_, secondary_ etc sources is a great name for these, since
  #   since it's really "History that has this as a primary source"
  has_many :primary_sources, class_name: ViolatorUnitHistory, primary_key: :id, foreign_key: :source_1_id
  has_many :secondary_sources, class_name: ViolatorUnitHistory, primary_key: :id, foreign_key: :source_2_id
  has_many :tertiary_sourcs, class_name: ViolatorUnitHistory, primary_key: :id, foreign_key: :source_3_id
...
end 

然后你的测试会变成:

describe "relationships" do
  expect(source).to have_many :primary_sources
  expect(source).to have_many :secondary_sources
  expect(source).to have_many :tertiary_sources
end

另一种方法是规范化您的数据库,以便您有一个包含ViolatorUnitHistory.idSource.id的连接表,以及一些常规指标(source_order或其他内容,适用于您的第1,第2,第3)< / p>

披露:我与Chris合作,我已经看到了实际的数据源,所以我对这个问题有了更多的了解