我正在为模型福利编写一个测试用例。该类文件包含一个 after_commit 回调,它调用 update_contract 方法。它还具有belongs_to:contract,touch:true。
@contract 是在规范的之前操作中创建的。
def update_contract
return unless {some condition}
contract.touch
end
it 'should touch contract on benefit creation when company is active' do
allow(benefit).to receive(:update_contract)
allow(@contract).to receive(:touch)
benefit = create(:benefit, benefit_type: :ahc, contract_id: @contract.id)
expect(benefit).to have_received(:update_contract)
expect(@contract).to have_received(:touch)
end
当我手动添加高于预期的触摸逻辑时,它响应了have_received。
我已经尝试过了
benefit.run_callbacks(:commit), use_transactional_fixtures is false in the system.
benefit 收到正常工作的 update_contract 方法。但@contract 还没有收到。
这确实有效
@contract 是在规范运行时创建的,之后不久就创建了好处
original_updated_at = @contract.updated_at
:created_benefit
@contract.updated_at != original_updated_at
它们的差异以微秒为单位。
Rspec 无法在 after_commit 回调中触发方法
是的。正在调用触发器。
但是,您的测试预期不会起作用。首先你像这样设置:
allow(benefit).to receive(:update_contract) # 1
allow(@contract).to receive(:touch) # 2
benefit = create(:benefit, benefit_type: :ahc, contract_id: @contract.id) # 3
这不会通过:
expect(benefit).to have_received(:update_contract)
因为您在第 1 行设置的间谍与第 3 行是不同的对象。
这不会过去:
expect(@contract).to have_received(:touch)
因为您在 2 行设置的间谍与
Benefit#update_contract
中模型获取的对象是不同的对象。
首先让我回答一下你实际问的问题。让我们验证一下是否正在调用
touch
before do
@contract = create(:contract ....)
end
it 'should touch contract on benefit creation when company is active' do
# Don't save it to the database yet, so no callbacks are triggered.
benefit = build(:benefit, benefit_type: :ahc, contract_id: @contract.id)
allow(benefit).to receive(:update_contract)
# Make sure we return the same object!
allow(benefit).to receive(:contract).and_return(contract)
allow(@contract).to receive(:touch)
# Or you could call `save` here. Both should work.
benefit.run_callbacks(:commit)
expect(benefit).to have_received(:update_contract)
expect(@contract).to have_received(:touch)
end
我不喜欢您当前的测试方法,因为它正在测试实现,而不是行为。
有些人可能会认为您当前的方法实际上更好,因为它可以在不接触数据库的情况下运行,但这里有另一种方法:
before do
@contract = create(:contract ....)
end
it 'should touch contract on benefit creation when company is active' do
original_updated_at = @contract.updated_at
create(:benefit, benefit_type: :ahc, contract_id: @contract.id)
expect(@contract.reload.updated_at).not_to eq(original_updated_at)
end
具体如何写有很多变体,例如您可以使用
freeze_time
并检查 exact 时间戳。或者您可以 以稍微不同的方式格式化测试,用块调用 expect
,并按照预期给出 from
和 to
。
但是无论你怎么做,根本的区别是:我不知道/关心 after_commit
回调的 implementation
是什么。我所关心的是时间戳发生变化的行为。