gpt4 book ai didi

ruby-on-rails - 如何使用 RSpec 在范围内模拟 any_instance?

转载 作者:数据小太阳 更新时间:2023-10-29 08:39:09 25 4
gpt4 key购买 nike

我正在尝试编写一个规范,该规范期望在一个范围内的所有实例上调用一个方法。找不到优雅的方式来做到这一点。

这是我的代码的简化表示:

class MyClass < ActiveRecord::Base

scope :active, where(:status => 'active')
scope :inactive, where(:status => 'inactive')

def some_action
# some code
end

这个类被另一个调用 MyClass.all 上的 some_action 的类使用:

class OtherClass

def other_method
MyClass.all.each do |item|
item.some_action
end
end

我想把它改成:

class OtherClass

def other_method
MyClass.active.each do |item|
item.some_action
end
end

为了测试这种行为,我可以简单地MyClass.stub(:active),返回一个 stub 数组,并在每个 stub 上期望some_action。但我不喜欢这种方法,因为它暴露了太多的实现细节。

我觉得更优雅一点的是 any_instance_in_scope。然后我可以简单地将我的规范写成:

MyClass.any_instance_in_scope(:active).should_receive(:some_action)

有什么办法可以实现吗?

最佳答案

首先,MyClass.all.some_action 不会起作用,因为 MyClass#some_action 是一个实例方法,同时 MyClass#all 返回一个 Array - 所以当您执行 MyClass.all.some_action 时,您实际上是在调用 Array#some_action

另外,请注意 MyClass.allMyClass.active 返回不同的类:

MyClass.active.class # => ActiveRecord::Relation
MyClass.active.all.class # => Array

我不确定你的 some_action 应该做什么......我想你可能想要做的一些选择:

选项 #1:缩小数据库查询范围

如果 some_action 正在过滤数组,您应该将其转换为另一个作用域,执行如下操作:

class MyClass < ActiveRecord::Base
scope :active, where(:status => 'active')
scope :inactive, where(:status => 'inactive')
scope :some_action, ->(color_name) { where(color: color_name) }
end

然后使用 MyClass.active.some_action('red').all 调用它。如果您只想要第一个结果,MyClass.active.some_action('red').first

如何使用 RSpec 测试 scope

这是一个很好的答案(以及原因):Testing named scopes with RSpec .

选项 #2:在实例之上执行操作

假设您确实希望将 MyClass#some_action 定义为实例方法。然后,您可以尝试这样做:

class MyClass < ActiveRecord::Base
scope :active, where(status: 'active')
scope :inactive, where(status: 'inactive')

def some_action
self.foo = 'bar'
self
end
end

在这种情况下,您可以使用 MyClass.active.last.some_action 执行它,因为 #last 将返回一个实例,而不是整个数组。

如何使用 RSpec 测试 some_action

我相信你应该简单地用期望来测试它:

MyClass.should_receive(:some_action).at_least(:once)
MyClass.active.last.some_action

对此的补充讨论:How to say any_instance should_receive any number of times in RSpec

选项#3:大规模行动

假设您真的想运行 MyClass.active.some_action。我建议您首先尝试这个(与选项 #2 相同的示例):

class MyClass < ActiveRecord::Base
scope :active, where(status: 'active')
scope :inactive, where(status: 'inactive')

def some_action
self.foo = 'bar'
self
end
end

然后用 MyClass.active.all.map{|my_class| 运行my_class.some_action .

现在,如果您真的想要实现MyClass.active.some_action——您希望some_action在ActiveRecord的所有实例上执行::Relation(我不推荐),这样做:

class MyClass < ActiveRecord::Base
scope :active, where(status: 'active')
scope :inactive, where(status: 'inactive')

def some_action
# really do it
end
end

还有……

class ActiveRecord::Relation
# run some_action over all instances
def some_action
to_a.each {|object| object.some_action }.tap { reset }
end
end

同样,我不建议这样做

如何使用 RSpec 测试 some_action

与选项 #2 相同的情况:

MyClass.should_receive(:some_action).at_least(:once)
MyClass.active.last.some_action

注意:所有代码均使用 Ruby 2.0.0-p0。安装并使用它,它的乐趣! :-)

关于ruby-on-rails - 如何使用 RSpec 在范围内模拟 any_instance?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15374749/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com