You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确模拟Active Record模型实例以测试方法调用?

问题场景

现有如下Rails控制器动作:

def create
  my_foo = MyFoo.find params[:foo_id]

  my_bar = my_foo.my_bars.create! my_bar_params
  my_bar.send_notifications

  redirect_to my_bar
end

需要在测试中断言my_bar(Active Record模型实例)的send_notifications方法被调用,但面临以下困境:

  • 直接验证通知发送(如expect { request}.to enqueue_mail ...)会穿透模型层抽象,不符合良好实践;
  • 使用expect_any_instance_of写法简洁,但已被RSpec官方标记为弃用;
  • 手动模拟Active Record内部方法需要设置大量allow规则,操作繁琐。

提问:是否存在其他测试方式,既无需模拟大量Active Record内部方法,也无需修改控制器动作代码?

可行的测试方案

1. 用instance_spy配合关联链模拟

通过模拟MyFoo实例及其关联的create!方法,返回一个间谍对象(instance_spy会自动允许所有未显式设置的方法调用),无需手动模拟Active Record内部方法:

it 'sends the notifications' do
  # 模拟MyFoo.find返回测试替身
  my_foo = instance_double(MyFoo)
  allow(MyFoo).to receive(:find).with(params[:foo_id]).and_return(my_foo)

  # 创建MyBar间谍,模拟关联create!返回它
  my_bar = instance_spy(MyBar)
  allow(my_foo).to receive_message_chain(:my_bars, :create!).and_return(my_bar)

  # 断言send_notifications被调用
  expect(my_bar).to receive(:send_notifications)

  post my_bars_path, params: { foo_id: params[:foo_id], my_bar: valid_attributes }
end

2. 基于真实实例的部分替身拦截

使用真实的模型实例,仅拦截create!方法返回目标实例,避免大量模拟:

it 'sends the notifications' do
  # 预先创建真实的MyFoo实例
  my_foo = MyFoo.create!(valid_foo_attributes)

  # 初始化MyBar实例,拦截关联的create!方法返回它
  my_bar = MyBar.new(valid_attributes)
  allow(my_foo.my_bars).to receive(:create!).and_return(my_bar)
  
  expect(my_bar).to receive(:send_notifications)

  post my_bars_path, params: { foo_id: my_foo.id, my_bar: valid_attributes }
end

3. 后置断言配合间谍

将断言放在请求执行之后,使用have_received验证方法调用,逻辑更贴近执行流程:

it 'sends the notifications' do
  my_foo = instance_double(MyFoo)
  allow(MyFoo).to receive(:find).and_return(my_foo)

  my_bar = instance_spy(MyBar)
  allow(my_foo.my_bars).to receive(:create!).and_return(my_bar)

  post my_bars_path, params: { foo_id: 1, my_bar: valid_attributes }

  expect(my_bar).to have_received(:send_notifications)
end

内容的提问来源于stack exchange,提问作者Daniel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 07:05:43