Ruby on rails RSpec看不到对ActiveRecord对象的更改,语法为'change(object,:message)`

Ruby on rails RSpec看不到对ActiveRecord对象的更改,语法为'change(object,:message)`,ruby-on-rails,activerecord,integration-testing,rspec-rails,Ruby On Rails,Activerecord,Integration Testing,Rspec Rails,我有三种型号: 用户 公司 承诺 承诺是针对用户和公司的HABTM加入表(即,当用户加入公司时,它会创建一个新的承诺)。它还有几个额外的列/属性: admin(用户是否具有此公司的管理员权限?) 由\ u管理员确认\(公司管理员是否已确认此用户加入公司的请求?) 由成员确认(用户本人是否确认了加入公司的邀请?) 我还定义了一种方便的方法来快速确定承诺是否得到充分确认: class Commitment < ApplicationRecord def confirmed?

我有三种型号:

  • 用户
  • 公司
  • 承诺
承诺
是针对
用户
公司
的HABTM加入表(即,当
用户
加入
公司
时,它会创建一个新的
承诺
)。它还有几个额外的列/属性:

  • admin
    (用户是否具有此公司的管理员权限?)
  • 由\ u管理员确认\
    (公司管理员是否已确认此用户加入公司的请求?)
  • 由成员确认(用户本人是否确认了加入公司的邀请?)
我还定义了一种方便的方法来快速确定承诺是否得到充分确认:

class Commitment < ApplicationRecord
  def confirmed?
    confirmed_by_admin? && confirmed_by_member?
  end
end
当RSpec测试变更时,
carol.commonments.first
似乎没有被重新加载-我得到以下测试输出:

Failure/Error:
  expect do
    patch commitment_path(carol.commitments.first),
          params: { commitment: { confirmed_by_member: true } }
  end.to change(Commitment.find_by(user: carol, company: company), :confirmed?).from(false).to(true)

  expected #confirmed? to have changed from false to true, but did not change
# ./spec/requests/commitments_spec.rb:69:in `block (3 levels) in <top (required)>'
故障/错误:
期望做
补丁承诺路径(carol.Commissions.first),
参数:{承诺:{成员确认}
结束。更改(承诺。查找人(用户:carol,公司:company),:已确认?)。从(假)。更改为(真)
预计#确认?从虚假变为真实,但没有改变
#./spec/requests/commissions_spec.rb:69:in `分块(3级)'
有什么好处?显然,我可以坚持大括号/块语法,但我想理解为什么一个有效,而另一个无效。

当我自己检查并尝试一个新的rails项目复制您的场景,并且失败时,我相信失败的原因是

  • .change
    的“块”形式将运行两次(“在
    expect
    块之前”和“在
    块之后”),无论该块内部是什么:

    .change{carol.commonments.first.confirmed?}

  • .change
    的“方法”形式对第一个参数运行一次:
    carol.commissions.first
    ,但对第二个参数运行两次
    :confirm?
    。但是,问题在于,此时规范文件中的
    carol.commissions.first
    与在
    commissions\u controller\update
    中实际更新的对象不共享相同的内存空间(该对象很可能被命名为
    @commission
    )。虽然它们是相同的
    承诺
    记录,但它们是单独的实例,并且当另一个发生更改时,另一个的属性值不会自动更改

考虑以下演示此“方法”表单工作的场景:

it 'sometest' do
  commitment = FactoryGirl.create(:commitment, user: carol,
                                  company: company,
                                  confirmed_by_admin: true)
  expect do
    # this commitment object is exactly the same object passed in the `change` below
    commitment.confirmed_by_member = true
  end.to change(commitment, :confirmed?).from(false).to(true)
end

免责声明:这是未经验证的,但因为它太复杂,我无法将其作为注释(使用所有示例测试代码)来编写,所以我将其作为答案写在这里。如果有人知道得更好,请一定要告诉我。

是的,我想就是这样。这似乎是最有可能的解释-
change
matcher不会在
expect
块完成后重新加载传递给它的ActiveRecord对象。我将在GitHub上提交一个问题,只是为了再次检查,但我准备标记此问题已解决,除非其他人有更多可验证的见解。感谢反馈!很高兴知道你们也有同样的想法:)@RyanLue:这个问题很可能会被拒绝。RSpec不知道也不关心您是否向其传递activerecord对象。它可以是任何对象,但也许rspec rails应该这样做?
it 'sometest' do
  commitment = FactoryGirl.create(:commitment, user: carol,
                                  company: company,
                                  confirmed_by_admin: true)
  expect do
    # this commitment object is exactly the same object passed in the `change` below
    commitment.confirmed_by_member = true
  end.to change(commitment, :confirmed?).from(false).to(true)
end