能否使用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提供的方法,可以直接存根链式调用的最终方法,省去模拟每一步的麻烦。
额外提醒
- 注意你测试里的
expected_result计算:你写的是100,但按照main_value=3750和sum返回7500的话,实际结果应该是((3750-7500)/7500)*100 = -50,记得修正这个预期值哦。 - 因为
billed_amount_last_month_change是私有方法,你用send调用是没问题的,但如果可以的话,最好通过公共方法来触发它,这样测试更贴近实际使用场景。
备注:内容来源于stack exchange,提问作者pinkfloyd90
相关产品推荐
相关产品推荐

