-
-
Notifications
You must be signed in to change notification settings - Fork 394
Improve change
matcher detection of objects that have changed.
#1132
Conversation
Maybe we can write a benchmark? If there is a cost. Maybe we should make this change available via configuration flag? |
I'm still trying to grasp The following naïve change: def matches?(event_proc)
- perform_change(event_proc) && @change_details.changed? && @matches_before && matches_after?
+ perform_change(event_proc) && @matches_before && matches_after?
end only breaks a few of the examples, that leads me to a thought it might be possible to use Not sure if it's technically feasible though. |
The change matcher itself has the semantic of the result under test having had to have changed, there are a lot of qualifiers that could pass without the underlying object changing so I don't want to rely on them |
157e9b5
to
91e5b65
Compare
I ran benchmarks on the hash calculation method
So even with a really large object with 10'000 instance variables we can gather the hash in under 10 ms so I'm happy merging this if you are |
/cc @benoittgt |
Thanks a lot for the benchmark! LGTM |
change
mather detection of objects that have changed.change
matcher detection of objects that have changed.
This seems to break some existing specs. [10] pry(#<RSpec::Matchers::BuiltIn::ChangeDetails>)> @actual_before.instance_variables.select { |v| @actual_before.instance_variable_get(v) != @actual_after.instance_variable_get(v) }
=> [:@attributes,
:@association_cache,
:@transaction_state,
:@_new_record_before_last_commit,
:@errors,
:@_already_called,
:@_database_validations_fallback,
:@new_record_before_save,
:@mutations_before_last_save,
:@attributes_changed_by_setter]
[11] pry(#<RSpec::Matchers::BuiltIn::ChangeDetails>)> @actual_before.instance_variable_get :@transaction_state
=> #<ActiveRecord::ConnectionAdapters::TransactionState:0x00007fad88571100
@children=
[#<ActiveRecord::ConnectionAdapters::TransactionState:0x00007fad898d88d8
@children=[],
@state=:committed>],
@state=:committed>
[12] pry(#<RSpec::Matchers::BuiltIn::ChangeDetails>)> @actual_after.instance_variable_get :@transaction_state
=> nil Those are service instance variables that are semantically unrelated to the object itself and doesn't really change its state. I'm not certain if it improves things, since now some of the negated change expectations start failing: expect { some_operation_that_might_or_might_not_change }.not_to change { user.profile } |
Do we miss regression tests? |
No, its just more accurate than it was before, I mean the object is changing, its just a question of if this is a semantically change object or an actually changed one |
@JonRowe For some reason, this change: @actual_before != @actual_after ||
@before_hash != @actual_hash ||
@before_attribute_hashes != @after_attribute_hashes didn't make it into 3.9.2: @actual_before != @actual_after || @before_hash != @actual_hash even though it's there on I've tested, it does resolve rspec/rspec-rails#1173. There's an edge case - when no qualifier is chained, it passes. specify do
widget = Widget.create(name: 'widget', package: 1)
expect {
# widget.update(name: 'A new name', package: 2)
}.to change { Widget.find(widget.id) }
end I'm still concerned if this change should be released, as it may break Rails |
This whole PR is unreleased due to concerns about bugs / performance issues. |
Really I should revert it from master. |
This has been reverted due to performance concerns, if someone wants to tackle this again (even using this as a base) please do! |
If an object is
==
and it'shash
results are==
it can still have changed, as it seems instance variable contents are not considered changes. One solution to this problem is to also compare the hashes of the instance attributes. This might have some performance problems so I'm not totally convinced this is the go but it does solve the problem.Fixes #1131