RSpec 测试仅期望 ActiveRecord 模型的某些属性发生变化

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

我正在成功测试 ActiveRecord 模型的某些属性是否已更新。我还想测试是否只有这些属性发生了变化。我希望我可以连接到模型的

.changes
.previous_changes
方法来验证我期望更改的属性是唯一要更改的属性。

更新

寻找与以下内容等效的东西(这不起作用):

it "only changes specific properties" do
  model.do_change
  expect(model.changed - ["name", "age", "address"]).to eq([])
end
ruby-on-rails rspec rspec-rails
3个回答
3
投票

尝试这样的事情

expect { model.method_that_changes_attributes }
  .to change(model, :attribute_one).from(nil).to(1)
  .and change(model, :attribute_two)

如果更改的不是属性,而是关系,您可能需要重新加载模型:

# Assuming that model has_one :foo
expect { model.method_that_changes_relation }
  .to change { model.reload.foo.id }.from(1).to(5)

编辑:

经过OP评论的一些澄清后:

那么你可以这样做

# Assuming, that :foo and :bar can be changed, and rest can not

(described_class.attribute_names - %w[foo bar]).each |attribute|
  specify "does not change #{attribute}" do
    expect { model.method_that_changes_attributes }
      .not_to change(model, attribute.to_sym)
    end
  end
end

这基本上就是您所需要的。

此解决方案有一个问题:它将为每个属性调用

method_that_changes_attributes
,这可能效率低下。如果是这种情况 - 您可能想要创建自己的匹配器来接受一系列方法。从这里开始


1
投票

也许这可以帮助:

model.do_change
expect(model.saved_changes.keys).to contain_exactly 'name', 'age', 'address'

这应该也适用于

.previous_changes

如果未保存更改,则

.changed
应该可以工作。

归根结底,这实际上取决于事情如何发生

do_change


0
投票

截至撰写本文时,我们可以做到,

  let!(:model) { create(:user, name: 'old_name', age: 29, address: 'old_address')
  let(:name) { 'New name' }
  let(:age) { 30 }
  let(:address { 'new address' }

  it { expect { model.do_change; model.reload }.to_not change {
    model.attributes.slice!("name", "age", "address", "updated_at") } }

  it { expect { model.do_change; model.reload }\
    .to change { model.name }.from('old_name').to(name)\
    .and change { model.age }.by(1)\
    .and change { model.address }.from('old_address').to(address) } }

这就是我解决@Greg 的答案中提到的低效率问题的方法,但根据要检查的属性数量,两种方法都可以更快,

但请确保为

model.create
分配不同的初始值,并注意
updated_at
,有时会被视为未更改(忽略部分毫秒)。

附注如果有人知道如何将这两者结合在一个测试中,请发表评论。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.