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

能否使用Minitest存根方法内部的变量?

能否使用Minitest存根方法内部的变量?

你遇到的问题其实不是存根局部变量,而是你的存根没有命中方法里实际调用sum的那个对象。我来帮你拆解一下:

你的billed_amount_last_month_change方法里,previous_month_amount是通过两次链式调用where之后的ActiveRecord关系来调用sum的,也就是:

account.invoices.where(...).where(...).sum(...)

而你测试里存根的是@account.invoices.expects(:sum),这相当于给原始的invoices集合存根,但实际调用sum的是经过两次where筛选后的关系对象,所以你的存根根本没被触发,自然还是用fixtures的数据。

下面给你几个可行的解决方案,覆盖不同的测试场景:


方案一:用Mock模拟完整的链式查询

这种方式最精准,能完全控制每一步的调用:

test "#billed_amount_last_month_change returns correct Float when values are valid" do
  @account = accounts(:one)
  main_value = 3_750
  billing_calculator = Metrics::Calculators::Kpis::Billing.new(@account, main_value)

  # 创建一个Mock对象,模拟ActiveRecord关系的链式调用
  mock_invoice_relation = Minitest::Mock.new

  # 第一次where(按due_date筛选):返回自身
  mock_invoice_relation.expect(
    :where, 
    mock_invoice_relation, 
    [{ due_date: (Time.current - 60.days)..(Time.current - 30.days) }]
  )
  # 第二次where(按status筛选):返回自身
  mock_invoice_relation.expect(
    :where, 
    mock_invoice_relation, 
    [{ status: %w[paid unpaid scheduled dunning refunded charged_back] }]
  )
  # sum方法返回750000(注意是分,因为方法里要除以100得到7500)
  mock_invoice_relation.expect(:sum, 750000, ["amount_cents"])

  # 用stub块替换account.invoices的返回值,块结束后自动恢复
  @account.stub(:invoices, mock_invoice_relation) do
    # 计算预期结果:((3750 - 7500)/7500.to_f)*100 = -50.0
    expected_result = -50.0
    result = billing_calculator.send(:billed_amount_last_month_change)
    assert_equal expected_result, result
    # 验证所有Mock的期望都被正确调用
    mock_invoice_relation.verify
  end
end

方案二:直接存根sum的结果(更简洁)

如果不需要验证where的调用细节,只是想控制sum的返回值,可以用更简洁的方式:

test "#billed_amount_last_month_change returns NaN when previous month amount is 0" do
  @account = accounts(:one)
  main_value = 3_750
  billing_calculator = Metrics::Calculators::Kpis::Billing.new(@account, main_value)

  # 存根经过两次where后的sum返回0(分),这样previous_month_amount就是0
  # 这里用stub_chain来处理链式调用
  @account.stub_chain(:invoices, :where, :where, :sum, 0) do
    result = billing_calculator.send(:billed_amount_last_month_change)
    assert result.nan?
  end
end

test "#billed_amount_last_month_change returns Infinity when previous month amount is 0 and past amount is positive" do
  @account = accounts(:one)
  main_value = 5_000
  billing_calculator = Metrics::Calculators::Kpis::Billing.new(@account, main_value)

  @account.stub_chain(:invoices, :where, :where, :sum, 0) do
    result = billing_calculator.send(:billed_amount_last_month_change)
    assert result.infinite? == 1 # 1表示正无穷
  end
end

这里的stub_chain是Minitest提供的方法,可以直接存根链式调用的最终方法,省去模拟每一步的麻烦。


额外提醒

  1. 注意你测试里的expected_result计算:你写的是100,但按照main_value=3750和sum返回7500的话,实际结果应该是((3750-7500)/7500)*100 = -50,记得修正这个预期值哦。
  2. 因为billed_amount_last_month_change是私有方法,你用send调用是没问题的,但如果可以的话,最好通过公共方法来触发它,这样测试更贴近实际使用场景。

备注:内容来源于stack exchange,提问作者pinkfloyd90

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 11:09:27