【问题标题】:Nil Associations with Rails Fixtures... how to fix?与 Rails Fixtures 的 Nil 关联...如何解决?
【发布时间】:2018-11-03 01:02:57
【问题描述】:

我有一个使用 rspec/fixtures 的 Rails 5.1 项目,我无法让fixture 加载与 belongs_to/has_one/has_many 关联的对象:我请求该fixture 的对象返回时,它的_id 列充满了看似随机的编号,ActiveRecord 将关联视为nil。这发生在具有许多关联的大型类以及只有少数字段的小型数据类上。

如果在我的测试代码中,我将这些关联分配给普通的 Ruby 代码,则对象表现正常并且我的测试通过。但是,当通过夹具加载相同的数据时,关联的记录不可用,并且需要跨越关联的数据的测试会失败。

例如,这里有两个受影响的类:

#app/models/location.rb
class Location < ActiveRecord::Base
  has_many :orders
  has_many :end_user
  belongs_to :retailer
  belongs_to :depot
end

#app/models/retailer.rb
class Retailer < ActiveRecord::Base
    has_many :locations
end

这里有两个对应的fixtures文件:

#spec/fixtures/locations.yml
loc_paris:
  retailer: ret_europe (Retailer)
  name: "Paris"
  nickname: "paris"

loc_washington:
  retailer: ret_usa (Retailer)
  name: "Washington"
  nickname: "washington"

#spec/fixtures/retailers.yml
ret_europe:
  name: "AcmeCo France"
  nickname: "acmecofr"
  currency_type: "EUR"

ret_usa:
  name: "AcmeCo USA"
  nickname: "acmecousa"
  currency_type: "USD"

根据以上数据,运行pp locations(:loc_paris)会得到:

#<Location:0x0000000006eee1d8
 id: 35456173,
 name: "Paris",
 nickname: "paris",
 retailer_id: 399879241,
 created_at: Wed, 23 May 2018 22:39:56 UTC +00:00,
 updated_at: Wed, 23 May 2018 22:39:56 UTC +00:00>

这些 id 编号在多次调用中是一致的,至少在相同的 RSpec 上下文中是这样。 (我将pp locations(:loc_paris) 放在let 块中。)然而pp locations(:loc_paris).retailer 返回nil

我尝试使用 FactoryBot,但我们不得不放弃它。我正在尝试对固定装置进行诚实的调整,但似乎我们最好在实际测试代码中简单地构建数据对象......因为该解决方案可以毫无怨言地工作:/

我在这里做错了吗?我们对固定装置的要求是否过高?

谢谢!

汤姆

【问题讨论】:

  • 在 rspec 运行中,ID 是“随机的”还是顺序更高?您可以在运行此测试的位置包含一个完整的 rspec 文件吗?在您测试 location.retailer 的同一块中,您是否也可以运行“pp 零售商(:ret_europe)”,运行 .valid?针对这两个对象,看看您是否有某种验证错误。总而言之,这通常应该有效,所以不知道为什么它不会。哦,您能否确认您也在表上实施参照完整性?
  • 虽然答案解决了问题,但我确实想指出,项目中的其他一些类(不是这些类)有一些在 before_validation 处理程序中运行的代码,它期望存在某些关联.在正常的应用程序工作流程中,这不是问题,但我注意到使用固定装置构建相同的对象可能会出现问题,因为不存在那些预期的关联。我会进一步研究,但这有点令人费解,因为包含的样本没有任何这样的逻辑并且仍然存在问题。
  • 另外,我不知道 postgres-enforced CREATE PRIMARY KEY 之类的,AR 应该在 Ruby 端处理它。还是随着新版本的 Rails 发生变化?该项目最初是从 4.​​1 升级而来的。

标签: ruby-on-rails ruby tdd fixtures


【解决方案1】:

夹具问题

查看您所做的,locations(:loc_paris) 会找到 locations.yml 中描述的记录,但 locations(:loc_paris).retailer 不会。

Rails 关联的工作方式如下:

locations(:loc_paris).retailer 将查找retailerretailer_idlocations(:loc_paris) 记录中提到的。在您的情况下 retailer_id: 399879241 并且没有 reseller 与此 id 这就是它返回 Nil 的原因。

解决方案: 像这样描述灯具:

#spec/fixtures/locations.yml
loc_paris:
  retailer_id: 1
  name: "Paris"
  nickname: "paris"

loc_washington:
  retailer_id: 2
  name: "Washington"
  nickname: "washington"

#spec/fixtures/retailers.yml
ret_europe:
  id: 1
  name: "AcmeCo France"
  nickname: "acmecofr"
  currency_type: "EUR"

ret_usa:
  id: 2
  name: "AcmeCo USA"
  nickname: "acmecousa"
  currency_type: "USD"

现在,locations(:loc_paris).retailer 将寻找locations(:loc_paris) 记录中提到的retailer_id 的零售商,即retailer_id: 1,并且有一个经销商ret_europe 与此id问题已解决

当您运行 rspec 时,首先 rspec 将这些装置保存到您的数据库中,其中包含一些自动生成的 id 值(如果未明确提供 id),这就是为什么 idreseller_id是一些随机值。如果您不希望 locations.yml 记录的 id 是某个随机值,您可以像这样自己提供:

loc_paris:
  id: 1
  retailer_id: 1
  name: "Paris"
  nickname: "paris"

提示: 由于rspectest 环境中运行(在app/spec/rails_helper.rb 中提到),并且正如我之前提到的,每当您运行rspec 时,首先它将固定装置保存到您的数据库中。如果您的localtest 数据库相同,则fixture 将替换您数据库的实际数据库记录。在您的情况下,locationsresellers 表记录中的记录将被完全擦除并替换为这些装置。因此,为test 环境创建不同的数据库。

希望这个答案有帮助

【讨论】:

  • 所以我考虑过这一点,但文档似乎暗示不需要指定您自己的 ID。如果我可以信任 ORM 或固定装置来处理它,我当然更愿意。我知道夹具知道该对象,因为如果我调用一个不存在的夹具,它会引发错误。尽管如此,这确实解决了问题,我已将此标记为答案。谢谢!
  • 我不知道你的 location_spec.rb 是什么样子的。但是,为了让您的代码在不实现我之前提到的解决方案的情况下工作,您应该在 location_spec.rb 中包含两个固定装置。 location_spec.rb 应该在您使用 pp locations(:loc_paris).retailer 的 RSpec 上下文中包含这些行 fixtures :locations fixtures :retailers
  • 和@TomCorelis 请从这些行中删除(Retailer) retailer: ret_europe (Retailer)retailer: ret_usa (Retailer)
猜你喜欢
  • 1970-01-01
  • 2017-07-11
  • 1970-01-01
  • 1970-01-01
  • 2011-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-19
相关资源
最近更新 更多