Rails Rspec allow_any_instance_of 为 ActiveRecord 对象属性引发 "does not implement" 错误

Rails Rspec allow_any_instance_of raises "does not implement" error for ActiveRecord object attributes

我有下一个问题。当我尝试使用 allow_any_instance_of 存根 ActiveRecord 模型的实例方法时,我收到错误消息 "Model does not implement #method",但如果我发送在此存根之前对数据库的创建或此模型的 select 实例对象的实际请求,我没有收到此消息,一切正常! 我在控制台中有同样的问题

Reloading...

>> Search.method_defined?(:tickets_count)
=> false

>> Search.method_defined?(:affiliate_id)
=> false

>> Search.last
  Search Load (3.9ms)  SELECT  "searches".* FROM "searches"  ORDER BY "searches"."id" DESC LIMIT 1
=> #<Search:0x007fe2aecf2900
 id: 515711,
 tickets_count: 1,
 affiliate_id: nil

>> Search.method_defined?(:affiliate_id)
=> true

>> Search.method_defined?(:tickets_count)
=> true

谁能解释一下,这到底是怎么回事?))

您在此处 运行 了解的是 activerecord 对数据库属性访问器方法的惰性实例化。

TL;DR 你可以使用这样的东西 https://github.com/rspec/rspec-rails/issues/1357

基本上 ActiveRecord::Base classes 不会定义任何基于数据库的列 setters/getters 直到他们需要,通常是通过缺少方法的挂钩,尽管可能有其他可能导致它们被定义的事情,例如调用 Search.find_by_affiliate_id 也可能导致定义加载。

我可以对 how/why 它的工作方式提供高级解释,但可能有更好/更新的资源可以更好地解释它,或者您可以通读ActiveRecord 源代码,虽然有点迟钝,但可以作为理解其行为的合理选择。

为什么不定义这些方法?

当通过从 ActiveRecord::Base 继承创建新的 ActiveRecord class 时,所有 class 都知道它是预期的 table 名称,但不是也不能'在不连接到数据库的情况下不知道 table 有哪些列。在不知道存在哪些列的情况下,它无法知道要定义哪个 readers/writers。 ActiveRecord 不会在加载时主动查询数据库,我认为这是一件非常好的事情,因为它允许您加载代码而无需迁移、连接的数据库。

ActiveRecord 所做的是挂接到 method_missingresponds_to? 等以确定何时在未定义的 ActiveRecord class 上调用方法,尝试加载当前数据库模式并为它找到的每一列自身定义 reader/writer/accessor 方法,然后重试方法调用以查看它现在是否已定义。

在您上面的示例中,很可能 不是 定义属性的 .last 调用,而是 .inspect 调用您的终端 运行s 以打印 Search 对象的实例。

要对此进行测试,您可以重新 运行 上面的示例,但将 Search.last 替换为 (s = Search.last).nil?。如果你这样做,我敢打赌 Search.method_defined?(:affiliate_id) 仍然是错误的。

希望这能解释为什么一开始没有定义这些方法,但后来又定义了。这也是您不能使用 alias_method :aliased_affiliate_id, :affiliate_id 的原因,因为在加载时没有定义方法 affiliat_id,这会失败。

对于您正在尝试做的事情,您需要决定是否值得为了 运行 这些规范而需要活动的数据库连接。

如果是这样,您可以触发您的 ActiveRecord classes 加载它们的 table 定义并在您的规范中定义它们的 reader/writer 属性以便通过。

有人 运行 遇到了与 rspec 相同的问题,使用猴子补丁解决方案加载了 activerecord classes:

的定义

https://github.com/rspec/rspec-rails/issues/1357