我不明白为什么rspec期望改变匹配器不工作,即使我复制了逻辑,我确信代码是正确的。
我有:
class SubscriptionItem < ApplicationRecord
after_commit :create_customer_quotas, on: :create
def create_customer_quotas
CustomerQuotaService.create_from_subscription_item! self
end
def self.create_from_subscription_item!(subscription_item)
customer = subscription_item.customer
subscription_item.quotas.each do |quota|
CustomerQuota.find_or_create_by! quota:, subscription_item:, customer:
end
end
在我的测试中,我有这个:
expect { subscription.save! }.to change(CustomerQuota, :count).by(subscription.quotas.count)
,我得到这个失败:
expected `CustomerQuota.count` to have changed by 1, but was changed by 0
但是,当我复制期望在测试中更改逻辑时,像这样:
count_before = CustomerQuota.count
subscription.save!
count_after = CustomerQuota.count
puts "before=#{count_before} && after=#{count_after}"
输出为:before=0 && after=1
所以它显然是有效的。为什么期望的改变没有像预期的那样起作用?
我怀疑这是由于after_commit
回调,因为如果我们使用事务性fixture,它不会在rspec中触发。
我们需要通过ar_object.run_callbacks(:after_commit)
手动触发after_commit回调
expect { subscription.save!; subscription.run_callbacks(:after_commit) }.to change(CustomerQuota, :count).by(subscription.quotas.count)
或者你可以使用test_after_commit gem,它会在事务提交后立即连接after_commit回调。绑定这个gem会自动运行after_commit回调,而不需要任何手动触发。在rails 5.0+
上不再需要这个了。如果你不想(或者你不能)修改代码,就用Sampat Badhe的答案(使用test_after_commit
或升级到rails>= 5.0.0),不要再读这篇文章了:这个答案是关于软件架构的,而不是关于解决你的rspec问题的。
但是我想知道的是....真的需要在after_commit
回调中完成吗?如果CustomerQuotaService.create_from_subscription_item! self
失败了怎么办?您已经将SubscriptionItem
提交到数据库。
您可以将回调更改为before_create :build_customer_quotas
在你的CustomerQuotaService
呼叫find_or_initializebuild
而不是find_or_create
。
这将确保所有内容都在一个事务中,并将正确保存或回滚。
更新:
一些可能有效的代码:
class SubscriptionItem < ApplicationRecord
before_create :build_customer_quotas
def build_customer_quotas
quotas.each do |quota|
customer_quotas.build quota: quota, customer: customer
end
end
end
当rails保存subscription_item
时,它将自动保存customer_quotas
。不需要额外的save
调用。
指出:
- 当
build_customer_quotas
被称为before_create
时,您不需要检查现有customer_quotas
的存在性 - 你没有提到你如何在你的服务中创建你要迭代的
quotas
。您可以使用类似的技术来构建它们并将它们链接到内存中的subcription_item
对象中。