ruby-on-rails RSpec测试仅期望ActiveRecord模型的某些属性发生更改

ijxebb2r  于 4个月前  发布在  Ruby
关注(0)|答案(3)|浏览(69)

我成功地测试了ActiveRecord模型的某些属性是否被更新。我还想测试只有这些属性被更改。我希望我可以挂钩到模型的.changes.previous_changes方法,以验证我希望更改的属性是唯一被更改的属性。

更新

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

it "only changes specific properties" do
  model.do_change
  expect(model.changed - ["name", "age", "address"]).to eq([])
end

字符串

p4rjhz4m

p4rjhz4m1#

试试这个

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,这可能效率很低。如果是这种情况-你可能想自己做一个接受方法数组的匹配器。从这里开始

tf7tbtn2

tf7tbtn22#

也许这能帮上忙:

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

字符串
这应该也适用于.previous_changes
如果未保存更改,则.changed应正常工作。
说到底,这实际上取决于do_change上的情况

irtuqstp

irtuqstp3#

在写这篇文章的时候,

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,它有时会被读取为未更改(忽略部分毫秒)。
P.S.如果有人知道如何在一个单一的测试中结合这两个合并,请评论.

相关问题