Skip to content
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

Enhance ObjectFormatter to transcribe values as their memoized let blocks or instance variable names #587

Open
walski opened this issue Nov 14, 2023 · 2 comments

Comments

@walski
Copy link

walski commented Nov 14, 2023

Subject of the issue

I am constantly spending time answering the question "which object is this?" when writing specs.

This is especially true for expectations on arrays or hashes, where one often deals with more than 1 value.

An example for this would be:

@one = Something.new(1)
@two = Something.new(2)
@three = Something.new(3)
@four = Something.new(4)

one_two_three = [@one, @two, @three]

expect(one_two_three).to contain_exactly(@one, @two, @four)

This would then report an error similar to this:

Failure/Error: expect(one_two_three).to contain_exactly(@one, @two, @four)
     
       expected collection contained:  ["#<struct Something id=1>", "#<struct Something id=2>", "#<struct Something id=4>"]
       actual collection contained:    [#<struct Something id=1>, #<struct Something id=2>, #<struct Something id=3>]
       the missing elements were:      ["#<struct Something id=4>"]
       the extra elements were:        [#<struct Something id=3>]

In this case it's pretty straight-forward to figure out the diff and the objects causing it. But this gets convoluted very fast with real world (e.g. ActiveRecord) objects.

Instead I would love if this would resemble something like this:

Failure/Error: expect(one_two_three).to contain_exactly(@one, @two, @four)
     
       expected collection contained:  [@one, @two, @four]
       actual collection contained:    [@one, @two, @three]
       the missing elements were:      [@four]
       the extra elements were:        [@three]

It should be fairly easy to do that at least for instance variables and let-blocks.

Would you be open to a PR in that direction? What would be a good approach to do this? Should this be an opt-in configuration? Should it be not in rspec-support at all and live in it's own gem?

Would love to hear your feedback :) Thank you!

Thorben

@JonRowe
Copy link
Member

JonRowe commented Nov 22, 2023

👋 I'm open to this, what would you propose happens if one or more of the elements is not objectively identical to the value from a let or instance variable? What happens in the case of mutation?

Some matchers, like I think the one you've used as an example provide their own diff rather than relying on rspec-support how do you propose to implement this for those matchers?

@walski
Copy link
Author

walski commented Nov 22, 2023

Hey, thanks for getting back!

what would you propose happens if one or more of the elements is not objectively identical to the value from a let or instance variable?

It should fall back to the representation that is used today (a.k.a. that inspect-like string).

What happens in the case of mutation?

I'm not sure if I understand that correctly. You are thinking about something like this:

# Assume a Rails request spec just for the sake of the example here

@betty = SomeActiveRecordModel.create(name: "Betty Smith")

# some code that calls a Rails controller and
# that controller modifies the record and sets the name
# to "Paula Jones" and returns it's ID as JSON

returned_id_from_response = JSON.parse(response.body)['id']

expect(SomeActiveRecordModel.find(returned_id_from_response)).to eq @betty

in which case the records would be equal to each other in ActiveRecord's logic due to them pointing to the same row in the database but would represent a different state of that row? I feel like that is a super bad example. Can you elaborate more what you really mean here? 😂

Some matchers, like I think the one you've used as an example provide their own diff rather than relying on rspec-support how do you propose to implement this for those matchers?

My understanding of the RSpec codebase is shallow at best, so I'd defer to the experts here. But in general I'd say this could be a process of adding such capability step by step in all the necessary places? And if this is not available everywhere from the get go, so be it. I think starting with these "container matchers" for arrays and hashes might be the best place to start. It's a place where you almost always deal with multiple objects, which makes it very cumbersome to work with the lengthy serialization of them in an inspect-like fashion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants