Skip to content

change doesn't handle ActiveRecord objects that well #1173

Open
@cupakromer

Description

@cupakromer

I ran into this today while trying to use the new have_attributes matcher:

widget = create(Widget, name: 'widget', package: 'single')
form_attributes = {
  id: widget.to_param,
  widget: {
    name: 'A new name',
    package: 'bulk',
  }
}

expect {
  put :update, form_attributes, valid_session
}.to change{ Widget.find(widget.id) }.to have_attributes(
  name: 'A new name',
  package: 'bulk',
)

Running this fails with:

expected result to have changed to have attributes { ... }, but did not change

Root Cause

The root of this issue is this line from the RSpec::Matchers::BuiltIn::ChangeDetails internal implementation used by the change matcher:

@actual_before != @actual_after

It's attempting to check that the objects aren't equal, thus not changing. However, ActiveRecord overrides == to mean do these objects have the same id value. If so, then they are the same object. This causes change to fail.

Workarounds

  • Call dup on ActiveRecord objects in the change block. This will remove the id resulting in ActiveRecord to fall back on a more intensive attribute match.
  • Call ActiveRecord#attributes to get the attribute list and use RSpec's standard hash matchers such as include.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions