Skip to content

Instantly share code, notes, and snippets.

@jaydorsey
Created December 2, 2024 14:08
Show Gist options
  • Save jaydorsey/6d139ee4dd531b8ec1ae93ad32ec1765 to your computer and use it in GitHub Desktop.
Save jaydorsey/6d139ee4dd531b8ec1ae93ad32ec1765 to your computer and use it in GitHub Desktop.
RSpec change record matcher
# frozen_string_literal: true
# This custom matcher is designed to make record changes easy
#
# Without this matcher you'd typically do one of:
#
# # I believe these won't always work, depends on object reloading, what is changed, how it's changed,
# # the order things got loaded, etc.
# expect { update }.to change(foo, :method)...
# expect { update }.to change { foo.method }...
# expect { update }.to change(foo.reload, :method)...
#
# This will always work
# expect { update }.to change { foo.reload, :method}...
#
# With this matcher it will automatically reload for you and you can use just it (and chain w/ from/to)
#
# expect { update }.to change_record(foo, :method)
# expect { update }.to change_record(foo, :method).from(nil)
# expect { update }.to change_record(foo, :method).from(nil).to(:bar)
RSpec::Matchers.define :change_record do |record, attribute|
supports_block_expectations
match do |block|
@before = record.reload.send(attribute)
block.call
@after = record.reload.send(attribute)
@before != @after && (@from.nil? || @before == @from) && (@to.nil? || @after == @to)
end
chain :from do |value|
@from = value
end
chain :to do |value|
@to = value
end
failure_message do
message = "expected #{record.class}##{attribute} to change"
message += " from #{@from}" if @from
message += " to #{@to}" if @to
message += ", but it did not"
message
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment