expect{ subject }.to change(...).by(1) 不起作用

expect{ subject }.to change(...).by(1) doesn't work

此测试通过:

it 'test' do
  old = parenthood.student.balance
  Payment.money_transfer_to_child current_user: parenthood.user,
                                  student_id: parenthood.student.id,
                                  amount: '1',
                                  comment: 'some comment'
  expect(parenthood.student.reload.balance).to eq(old+1)
end

改成下面的形式不通过("was changed by 0"):

subject { Payment.money_transfer_to_child current_user: parenthood.user,
                                              student_id: parenthood.student.id,
                                              amount: '1',
                                              comment: 'some comment' }
it { expect{ subject }.to change(parenthood.student.reload, :balance).by(1) }

我做错了什么?

在主题中添加一行解决了我的问题:

subject { Payment.money_transfer_to_child current_user: parenthood.user,
                                              student_id: parenthood.student.id,
                                              amount: '1',
                                              comment: 'some comment'
              parenthood.student.reload }

我看到您找到了让它工作的方法,但也许让我们在这里得到一个正确的答案。那么首先为什么它不起作用。

it { expect{ subject }.to change(parenthood.student.reload, :balance).by(1) }

执行此行时(不完全但大致):

  1. 首先计算改变方法的参数。这意味着 parenthood.student.reload 在该点被评估。
  2. 在计算的 object 上调用 balance 方法,并记住该值。
  3. expect 中的块(在本例中只有 subject)被执行
  4. 再次在 object
  5. 上调用平衡方法

这里的问题是 parenthood.student.reload 在执行 subject 之前被求值。在执行 subject 之后,它再也没有被计算过,因为它已经传递给了 change 方法。这就是为什么你在那里有旧值。

您可以做的是找到您的解决方案(但这不是很优雅,因为重新加载父母身份不是您正在测试的内容的一部分)。或者您可以使用更改匹配器中的块并执行以下操作:

it { expect{ subject }.to change { parenthood.student.reload.balance }.by(1) }

该语法使带有 parenthood.student.reload.balance 的块在 运行 subject 之前和之后都被评估,并且应该修复您的测试。