ruby-on-rails - 用 rspec 观察 `allow` 和 `have_received` 的奇怪行为
问题描述
我在使用allow
和have_received
在我的规格中遇到问题
我有一个名为的模型Obj
,它belongs_to
与一个名为Parent
. 该Parent
模型与 有has_many
关系Obj
。
在Obj
模型中,我定义了一个名为child_method
. 在Parent
模型中,我定义了一个名为 的方法calls_child_method
,它遍历Obj
与之关联的每个并让它们调用child_method
我正在编写一个规范来测试这种行为,如下所示,但它一直失败:
describe 'parent calls_child_method' do
let(:obj) { Obj.create }
before do
allow(obj).to receive(:child_method)
end
it 'should call child_method' do
obj.parent.calls_child_method
expect(obj).to have_received(:child_method)
end
end
输出:
expected: 1 time with any arguments
received: 0 times with any arguments
allow_any_instance_of
然而,当我用来做间谍/存根时,这似乎通过了:
describe 'parent calls_child_method' do
let(:obj) { Obj.create }
before do
allow(obj).to receive(:child_method)
end
it 'should call child_method' do
expect_any_instance_of(Obj).to receive(:child_method)
obj.parent.calls_child_method
end
end
或者如果我直接调用子方法:
describe 'parent calls_child_method' do
let(:obj) { Obj.create }
before do
allow(obj).to receive(:child_method)
end
it 'should call child_method' do
obj.child_method
expect(obj).to have_received(:child_method)
end
end
在所有这一切中,我已经验证了Obj
创建的实例实际上是child_method
通过byebug
调试来调用的,以查看它是否被调用。
有人可以帮我理解为什么规范/间谍会这样吗?
解决方案
这个问题困扰了我很多年。这就是我理解正在发生的事情的方式。
在您失败的情况下obj
,是对您新创建的Obj.create
. 通过调用expect(obj).to have_received(:child_method)
它预计obj
应该收到EXACT 引用child_method
。
当obj
的父级接收calls_child_method
到它时,它会遍历与其关联的每个 Obj。很可能它parent.objs
会触发新的数据库调用。在您的情况下,它将找到您的obj
,但会对它有不同的引用。而那个不同的参考将是得到的child_method
这就是为什么从技术上讲,您的对象确实收到了方法调用,但通过不同的引用。结果你的期望失败了:(
expect_any_instance_of(Obj)
解决问题。但是,通常最好避免它。
您的最后一个示例是成功的,因为您的期望和方法调用使用相同的对象/引用。
推荐阅读
- multithreading - Flask:多线程时如何写入 SQLAlchemy DB?
- javascript - 我如何将字段并排放置在嵌入中
- python-3.x - 无法使用 pip 在 Windows 上安装 h5py
- c# - 比较 SQL Server C# Windows 应用程序中两个表中的两个相似列
- c++ - 有多个空基时如何在 MSVC 中强制空基优化
- powerbi - 在 DirectQuery 模式下是否需要手动刷新仪表板上的磁贴?
- reactjs - 使 Select 上的标签可编辑
- c# - .NET 5.0 无法在 Visual Studio Code 上搭建 razorpage
- sql - sql从一系列位值制作几何序列
- c# - 如何将每个单词字母带入数组c#