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

Rspec测试ActiveRecord回调时特定实例方法断言失败如何解决

测试失败的核心原因

测试中通过let!创建的week_1_sr实例,和模型代码中SurveyRequirement.find_by查询返回的survey实例,是内存中两个完全独立的Ruby对象,二者仅对应数据库的同一条记录,实际object_id并不相同。
Rspec的消息期望是绑定在具体内存对象上的,你给week_1_sr.delighted_survey设置的调用监听,无法作用到业务代码中查询出来的新实例的关联对象上,因此测试失败。
而用expect_any_instance_of的测试可以通过,是因为该方法会给DelightedSurvey的所有实例添加调用监听,不管是哪个内存对象触发方法都能被捕获。

调整方案

方案1:Stub查询返回预创建的实例

直接将SurveyRequirement.find_by的返回值Stub为你提前创建的week_1_sr,保证业务代码中拿到的就是设置了期望的对象:

let!(:week_1_sr) { create(:survey_requirement, :week_1_survey) }

it "should fire a CSAT survey after week 1" do
  # 可添加参数匹配让Stub逻辑更严谨
  allow(SurveyRequirement).to receive(:find_by).with(
    position: [1, "*"],
    type_of_weighin: ["standard", "*"]
  ).and_return(week_1_sr)

  expect(week_1_sr.delighted_survey).to receive(:sendSurvey).once.with(
    client: client,
    additional_properties: {delay: 3600}
  )
  create(:check_in, client_id: client.id, type_of_weighin: "standard")
  create(:check_in, client_id: client.id, type_of_weighin: "standard")
end

方案2:全局监听+实例身份校验

如果不想Stub查询逻辑,也可以在全局监听的基础上校验调用的实例是否符合预期:

let!(:week_1_sr) { create(:survey_requirement, :week_1_survey) }

it "should fire a CSAT survey after week 1" do
  target_survey_id = week_1_sr.delighted_survey.id
  expect_any_instance_of(DelightedSurvey).to receive(:sendSurvey).once do |survey, args|
    # 校验调用的实例是否正确
    expect(survey.id).to eq target_survey_id
    # 也可以同时校验入参正确性
    expect(args[:client]).to eq client
    expect(args[:additional_properties][:delay]).to eq 3600
  end
  create(:check_in, client_id: client.id, type_of_weighin: "standard")
  create(:check_in, client_id: client.id, type_of_weighin: "standard")
end

额外优化建议

回调中获取position时建议对关联做reload,避免刚创建的记录未同步到关联缓存导致计算的索引错误:

position = client&.check_ins&.reload&.reverse&.index(self)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 01:18:02