Fix generated docstring for BeAnInstanceOf when Class#inspect changed #184

Merged
merged 5 commits into from Oct 27, 2012

Conversation

Projects
None yet
4 participants
Contributor

rentalcustard commented Oct 23, 2012

This fixes the situation illustrated in @ddd's gist https://gist.github.com/13469eb6a970e48ec1dc and reported in #rspec on Freenode, whereby a class defines .inspect with expanded information (e.g. all ActiveRecord model classes) and messes up docstring generation.

BeAnInstanceOf doesn't currently define its own #description method, so the default from BaseMatcher, which calls .inspect, was being called. This PR just patches in a description method to the matcher to ensure that to_s is called instead.

Member

soulcutter commented Oct 23, 2012

👍 Nice and clean.

+ def self.inspect
+ "User(id: integer, name: string)"
+ end
+ end
@myronmarston

myronmarston Oct 23, 2012

Owner

I don't think we want or need this constant defined outside this one context. Can you use an anonymous class (e.g. Class.new) and/or use stub_const if you want it assigned to a constant? That way we don't "leak" the constant outside of this example.

Contributor

rentalcustard commented Oct 23, 2012

@myronmarston good point, well made. See latest commit - it seemed easiest to use simple stubbing on an existing class once I explored the options. WDYT?

Owner

myronmarston commented Oct 23, 2012

That's an improvement. However, I kinda liked your example of a User class as it's more intention revealing of a real situation than Fixnum#inspect is (has anyone ever overriden inspect on Fixnum?)

Thoughts?

Contributor

rentalcustard commented Oct 23, 2012

I tried using Class.new to define a User class with the desired characteristics, but it felt strange to be defining self.to_s (to return the expected value) and self.inspect (to show the incorrect value). I felt like the test was at that point going to be relying on the matcher calling to_s, rather than something else like name.

So then I tried stub_const, but I couldn't use it with an uninitialized constant, so I wasn't sure where to go with that.

Did you have something specific in mind? I'd like to represent the intention accurately, as in the original code, but without leaking a constant.

Owner

myronmarston commented Oct 23, 2012

So then I tried stub_const, but I couldn't use it with an uninitialized constant, so I wasn't sure where to go with that.

I'm not sure what you mean by this, but I think this should work:

let(:user_klass) do
  Class.new do
    def self.inspect
      "User(id: integer, name: string)"
    end
  end
end

before { stub_const("User", user_klass) }

The name and to_s methods will work automatically due to how ruby classes work when they are first assigned to a constant.

Contributor

rentalcustard commented Oct 26, 2012

@myronmarston you're absolutely right, and having toyed with other approaches (just passing a stub directly to be_instance_of, for example), I think yours is the best. See latest commit. I'm happy to reauthor it since it's your code.

+
+ it "provides a description including only the class name" do
+ matcher = be_an_instance_of(User)
+ #it will be namespaced because I defined it inside this spec file
@alindeman

alindeman Oct 26, 2012

Contributor

Is this still accurate?

Contributor

rentalcustard commented Oct 26, 2012

God, I'm sloppy on Fridays. Thanks @alindeman.

myronmarston added a commit that referenced this pull request Oct 27, 2012

Merge pull request #184 from mortice/implicit-docstring-for-instance-…
…of-matcher

Fix generated docstring for BeAnInstanceOf when Class#inspect changed

@myronmarston myronmarston merged commit d5d073e into rspec:master Oct 27, 2012

1 check passed

default The Travis build passed
Details
Owner

myronmarston commented Oct 27, 2012

Thanks @mortice, this looks great. I just merged it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment