-
-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
allow inspectors to be registered for different objects #516
Conversation
What will diff output after this change?
Related: #365 |
I love the idea. Maybe we could specify in the failing output the custom inspector used? |
@pirj , I've updated the expected behaviour section to clarify what the diff would look like after registering the new inspector. Hopefully that answers your question but let me know if it doesn't. |
@benoittgt can you clarify in terms of implementation what you have in mind? Are you thinking we should differentiate between custom inspectors and default ones (the ones currently in
From what I understand of the code (and I could definitely be misunderstanding this), with the exception of arrays and hashes, currently it will always use an inspector to inspect any object, falling back to |
@b-loyola yes that was exactly what I was thinking when I saw your example.
Yes completely. Only custom one could be "noisy" by default. I am worried about having future issues on RSpec with people that use invalid custom inspector but doesn't know it is used in diff. Also I think this PR should be added in RSpec 4 only. :) But I think that first we should wait for @JonRowe feedbacks on your existing PR. ;) |
There are two problems here.
This wouldn't be a problem if:
2.1 Hashes
as you already mentioned, it's the diff that has a deficiency. Should have been: {
:foo => 60 seconds,
- :bar => 59 seconds,
+ :bar => 1 minute,
} For comparison,
A bit too verbose for my taste, but it doesn't suffer from either problems. 2.2 Arrays
Surely, it would better look as [
- 59 seconds,
+ 1 minute,
60 seconds
] For comparison,
Again, quite verbose. Speaking of custom inspectors, I'd love to highlight @benoittgt's concern:
The failure message output for Failure/Error: it { expect(1.minute).to equal(60.seconds) }
Diff:
- 60
+ 60
And stock RSpec wins the race here:
For those issues, custom differs could be an option, but it's quite a long way to catch up with |
@benoittgt that makes sense. But I think a potential pitfall with appending a hash1 = {
travel_duration: 1.hour,
meal_duration: 2.hours
}
hash2 = {
travel_duration: 3600,
meal_duration: 7000
}
expect(hash1).to eq(hash2) it fails with:
In this case, only
I think this means that to cover this case without bigger changes we'd need to use the same inspector for both |
@pirj thanks for the detailed feedback and all of the examples. I definitely agree and I wasn't really thinking of the object identity case here so I do see how that could be a potential pitfall. (2.hours - 60.minutes) == 1.hour
#=> true
(2.hours - 60.minutes) == 3600
#=> true
(2.hours - 60.minutes).inspect
#=> "2 hours and -60 minutes"
1.hour.inspect
#=> "1 hour"
3600.inspect
#=> "3600" So adding the ability to register a custom inspector is really just a way to enable a custom representation of an object for spec comparisons only (without the need to monkey-patch a class' As mentioned in the description this is actually currently possible to do as a "hack", by prepending a custom inspector to RSpec::Support::ObjectFormatter::INSPECTOR_CLASSES.unshift(MyCustomInspector) So the main goal of this PR is just to make this a better interface and potentially open up a path forward to do something like what is mentioned here, e.g.: RSpec.configure do |config|
config.inspectors.register(MyCustomInspectorForDuration)
config.inspectors.register(MyCustomInspectorForSomeOtherObject)
# etc
end |
From my (rather subjective and opinionated) point of view, objects should adhere to some protocol that consumers can rely on. In this case, it's "return the internal representation for I'm personally reluctant to decorate objects for diffing purposes, especially if it seems possible to keep the presentation under control by adhering to the above rule. Given that the differ is matcher-aware and doesn't solely rely on the In any case, the specs are too specific and don't indicate clearly what the difference custom inspectors make. I would ask to add more specs, but don't really want to waste your time. |
Thanks @pirj that's great feedback. While I agree with your opinion that objects should adhere to some protocol that consumers can rely on, my opinion is that it's not easy (if at all possible) to keep the presentation under control just by adhering to the protocol mentioned above. In fact, the From my (also very subjective and opinionated) point of view, it's better to allow users to customize the output to be the way they want it for specific objects than to introduce breaking changes in presentation that assume that an object should be sometimes presented with In a separate point, I checked what the differ does with regards to the object identity and it seems to handle that case pretty well since it shows that they are different objects:
This would still be pretty readable with the custom inspector:
Anyway, I'm happy to add more test cases here if that will help push this forward, but also don't want to waste more time if this is not going to be considered. In my opinion, while this is definitely not a complete solution it's a step in the right direction. If there's disagreement there then there's probably no point in continuing to work on this, but if we agree that this can provide value then I can definitely add more tests here. If that's the case just let me know what kind of specs you would like to see here. |
@b-loyola One more example I can think of. expect(state_change_margins).to eq([0.celsius, 100.celsius]) # celsius
|
@pirj that's kind of my point here. I don't think it's rspec's job to know how to represent every object out there, and allowing different codebases to customize how to represent objects for only for diffing purposes is what this PR is all about (or at least paving way for doing so). If in one codebase there is an object which is usually compared with integers, registering a custom inspector for that object to be displayed as an integer might make sense. If in another codebase there's an object that is usually compared to strings, there might be a desire to customize how that object is inspected for diffing. And in most cases they will probably just want what the default behaviour is, and let the representation just follow what And to voice my (again subjective) opinion here, while I know this is possible I kind of disagree with the idea of an object that can be both equal to |
Is it possible to achieve something similar and file-local via refinements? Not sure where to put "using" though. Below did not work
|
halostatue/diff-lcs#70 semi-related |
Closing as this is probably stale now, can reopen if there's any interest. |
Subject of the issue
When comparing objects nested in diffable objects (e.g.
Hash
), it can be difficult to differentiatebetween objects which are failing the comparison and objects which are considered equal but
have different outputs for
inspect
. One notable example isActiveSupport::Duration
where60.minutes
is considered equivalent to1.hour
, but produces different output forinspect
which can cause confusion and seem like it is failing the assertion when it is in fact just failing the
inspect comparison.
The proposal is to allow inspectors to be registered in
ObjectFormatter
so that custominspectors can be added for these classes, to improve the signal to noise ratio in diffs
when looking at failed tests.
At the moment it is possible to 'hack' this by manually prepending a custom inspector class
to
RSpec::Support::ObjectFormatter::INSPECTOR_CLASSES
, but here we create a morefriendly interface to allow this through something like:
If this gains enough traction this could be expanded to allow for configuring this in
RSpec's configuration, perhaps in a similar way to what is mentioned here.
Related to rspec/rspec-expectations#97
Your environment
Steps to reproduce
To see an example of failing specs which provide misleading diffs, run this example and
note how in the
'does not show in the diff when objects are equivalent'
example it seemsthat both
object1
andobject2
are failing the assertion, when in fact onlyobject2
is failing the assertion.
Expected behavior
We should be able to register a custom inspector, to be able to modify the output of inspect
for specific objects, so that it becomes easier to tell what is actually failing the assertion:
This would mean that after registering the custom inspector above, the diff would look like this:
Actual behavior
Diffs are based on the raw outoput of
inspect
, so it is hard to tell that the value inobject1
is not actually failing the assertion, and that only the value in
object2
is the issue: