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_missing
、responds_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:
的定义
我有下一个问题。当我尝试使用 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_missing
、responds_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:
的定义