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

如何调整请求测试以验证notify_referrer方法被调用?

如何验证notify_referrer方法被调用(避开allow_any_instance_of和assigns)

问题场景

控制器create方法逻辑如下:

def create
  @referral = Referral.new(referral_params)

  if @referral.save
    @referral.notify_referrer
    @referral.create_referral_created_event

    render json: @referral, status: :created
  else
    render json: @referral.errors.full_messages, status: :unprocessable_entity
  end
end

需要编写请求测试验证notify_referrer方法被调用,但测试运行前无法访问assigns(:referral),且不想使用allow_any_instance_of,现有测试片段如下:

it 'notifies the referrer' do
  allow(_____).to receive(:notify_referrer)

  post api_referrals_path, params:
  {
    referral: { referrer_id: referrer.id, referred_id: referred.id }
  }, headers: { HTTP_AUTHENTICATION: token }

  expect(assigns(:referral)).to have_received(:notify_referrer)
end

可行解决方案

方案1:使用实例替身(Instance Double)模拟控制器逻辑

通过创建Referral的实例替身,拦截控制器中Referral.new的调用,直接验证替身的方法调用情况,完全不需要依赖数据库或assigns:

it 'notifies the referrer' do
  # 创建Referral实例替身
  referral_double = instance_double(Referral)
  
  # 让Referral.new返回这个替身,用hash_including匹配参数(避免硬编码完整params)
  allow(Referral).to receive(:new).with(
    hash_including(referrer_id: referrer.id, referred_id: referred.id)
  ).and_return(referral_double)
  
  # 模拟save成功的场景
  allow(referral_double).to receive(:save).and_return(true)
  # 存根需要验证的方法
  allow(referral_double).to receive(:notify_referrer)
  allow(referral_double).to receive(:create_referral_created_event)

  # 发送请求
  post api_referrals_path, params:
  {
    referral: { referrer_id: referrer.id, referred_id: referred.id }
  }, headers: { HTTP_AUTHENTICATION: token }

  # 直接验证替身是否收到了notify_referrer调用
  expect(referral_double).to have_received(:notify_referrer)
end

方案2:请求后查询数据库实例并验证

如果希望测试真实的数据库写入逻辑,而非完全模拟,可以先存根notify_referrer方法,请求后从数据库中获取实例再验证:

it 'notifies the referrer' do
  # 存根Referral实例的notify_referrer方法,保留原逻辑同时记录调用
  allow_any_instance_of(Referral).to receive(:notify_referrer).and_call_original

  # 发送请求
  post api_referrals_path, params:
  {
    referral: { referrer_id: referrer.id, referred_id: referred.id }
  }, headers: { HTTP_AUTHENTICATION: token }

  # 从数据库中获取刚创建的Referral实例
  referral = Referral.find_by(referrer_id: referrer.id, referred_id: referred.id)
  
  # 验证该实例是否调用了notify_referrer
  expect(referral).to have_received(:notify_referrer)
end

方案3:使用Spy实例替代Double

如果觉得实例替身的配置太繁琐,可以用instance_spy简化,它会自动存根所有方法并记录调用:

it 'notifies the referrer' do
  # 创建Spy实例,自动存根所有方法并记录调用
  referral_spy = instance_spy(Referral)
  
  allow(Referral).to receive(:new).with(
    hash_including(referrer_id: referrer.id, referred_id: referred.id)
  ).and_return(referral_spy)
  
  allow(referral_spy).to receive(:save).and_return(true)

  post api_referrals_path, params:
  {
    referral: { referrer_id: referrer.id, referred_id: referred.id }
  }, headers: { HTTP_AUTHENTICATION: token }

  # 直接验证spy的方法调用
  expect(referral_spy).to have_received(:notify_referrer)
  expect(referral_spy).to have_received(:create_referral_created_event)
end

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 01:04:57