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
Make have_received complain if passed a block #364
Make have_received complain if passed a block #364
Conversation
I rather stupidly assumed I could use a block passed to have_received to make assertions about the arguments passed in a method call, in the same way that is possible with the 'receive' matcher. Ordinarily I wouldn't think such a mistake would warrant being guarded against in the test library itself, however, in this case the test silently passes, rather than erroring out. This patch makes have_received raise an exception if passed a block argument to make it clear that matching against arguments to the method call in this way is not possible.
it "raises an exception when a block is used to match the arguments" do | ||
dbl = double_with_met_expectation(:expected_method) | ||
expect { | ||
expect(dbl).to have_received(:expected_method) { |argument| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@timcowlishaw this can be reduced to one line:
`expect(dbl).to have_received(:expected_method) {}
@timcowlishaw thanks! BTW other RSpec superfriends: I worked with tim and gave him some pointers on this one. |
dbl = double_with_met_expectation(:expected_method) | ||
expect { | ||
expect(dbl).to have_received(:expected_method) { } | ||
}.to raise_error(/have_received matcher does not take a block argument/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that you are using caller[0]
here to specify the location causing the error, it'd be nice if you included that in the error regexp, to assert that it's always the right line. You can use __FILE__
and __LINE__
to avoid hardcoding the file/line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realised this is dumb, because it'll happen in the backtrace. Sorry @timcowlishaw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this might make more sense as a warning with caller(0). What do you think @JonRowe ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just think we need to spec which line these come from, as we've had a few instances where we've messed it up, or the ruby implementation has a different idea about caller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you think about warning versus raising though? We get the backtrace for free with a raise but it stops execution. I think a warning is a better solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, agreed on both counts. Assuming you wouldn't rather a PR allowing has_received
to take a block to match args (as proposed below), I'll add a spec and turn this into a warning!
Thanks,
Tim
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do this, we'll merge it (maybe into 2-14-maintenance
if others agree too), and if you feel like making it take a block to match args as another PR that'd be awesome :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great stuff. Will sort this out tomorrow!
I wonder if it would make sense for it to accept and use a block somehow. @timcowlishaw -- can you give us an example of how you were trying to use the block? It might be worth supporting. |
My first instinct was "what the, it can't take a block?" so I'd be in favour of allowing it to have a block. |
@JonRowe @myronmarston - I tend to agree here actually. The situation I had was something like this let(:missile_silo) { double(:missile_silo, :fire_missiles! => true) }
it "calls the missile silo with a user authorized to fire missiles" do
missile_command = MissileCommand.new(missile_silo)
missile_command.start_war!
expect(missile_silo).to have_received(:fire_missiles!) do |authorizing_user|
expect(authorizing_user.rank).to eq(:commander)
end
end Now, this seems like a pretty reasonable behaviour to me, and is in line with what Subject to this caveat though, I for one would be very pleased to be able to use a block with Cheers, Tim |
I'd be in favour of cleaning this up and merging into |
Cool, I will do both of those things. Thank you! |
I think your use case is reasonable. The reason we didn't already support this is because for Anyhow, I think this is kinda like the
I'm not sure I agree. If we're going to add support for blocks, I don't want to release code that tells users that blocks aren't supported, as that'll create confusion. I'm also not sure if this kind of fix belongs in a patch release; patch releases are meant to be bug fixes only, and this feels like a new feature to me. @alindeman, what do you think? |
I agree that we should support the example shown above. We currently document the ability to set custom expectations on fake implementations in the README, so I think it makes sense to unify it for spies too. Really nice example, @timcowlishaw. I think this is simply a new feature, but not a bug. I think the feature should go into 3.0 while we leave everything as-is in 2.14 and 2.99. |
I think that any implementation using the block is a new feature, but it'd be nice to warn that it doesn't do anything in |
…or file / line number
Right guys, I've amended this to warn rather than raise when a block has been passed, and have amended the regex in the spec to test for the presence of the filename and line number. I'll prepare a second PR which takes a block and yields the arguments for further assertions, and will leave it up to you whether or not you want to merge either, or both! Thanks a lot, Tim |
it "warns when a block is used to match the arguments" do | ||
dbl = double_with_met_expectation(:expected_method) | ||
self.stub(:warn => nil) | ||
expect(dbl).to have_received(:expected_method) { }; line = __LINE__ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice hack for avoid calculating the __LINE__
offset :)
@timcowlishaw thinks this can be closed because we decided not to merge the warning. |
I rather stupidly assumed I could use a block passed to have_received to make
assertions about the arguments passed in a method call, in the same way that is
possible with the 'receive' matcher. Ordinarily I wouldn't think such a mistake
would warrant being guarded against in the test library itself, however, in this
case the test silently passes, rather than erroring out. This patch makes
have_received raise an exception if passed a block argument to make it clear
that matching against arguments to the method call in this way is not possible.