Rspec 无法在 after_commit 回调中触发方法

问题描述 投票:0回答:1

我正在为模型福利编写一个测试用例。该类文件包含一个 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

它们的差异以微秒为单位。

ruby ruby-on-rails-4 rspec rspec-rails
1个回答
0
投票

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
中模型获取的对象是不同的对象。


如何修复它 - 选项 1

首先让我回答一下你实际问的问题。让我们验证一下是否正在调用

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

如何修复它 - 选项 2

我不喜欢您当前的测试方法,因为它正在测试实现,而不是行为

有些人可能会认为您当前的方法实际上更好,因为它可以在不接触数据库的情况下运行,但这里有另一种方法:

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

 是什么。我所关心的是时间戳发生变化的
行为

© www.soinside.com 2019 - 2024. All rights reserved.