rspec-mocks
rspec-mocks copied to clipboard
Add argument matcher for expecting a block.
This is @kaiwren's a_block
argument matcher with fleshed out sad path specs, and a refactoring of the match logic to allow mixing with other arguments. Review please @kaiwren @ioquatix @benoittgt.
Some further improvements could be made here,what happens when used positionally? and does it always mean the supplied &block
argument? After all object.msg(proc {}, proc {})
is valid Ruby, should we expect on this with .with(a_block, a_block)
and how do we differentiated that from msg(proc{}) { }
. And if we don't allow that should we reject the method expectation out of hand? We already have and_yield
for block arguments too.
My only use case was to do something like expect(thing).to receive(:method).with_block
, so anything that can do that I am happy with. It might even make sense to add that specific method with_block
as that's what I want to use initially. I think the extended syntax is also useful.
@ioquatix We already have that, it's called and_yield
, e.g. expect(thing).to receive(:method).and_yield
For some reason I couldn't find this initially... I did look for it!
Maybe we can say this is good enough for a first step? Ready the tests I found it already a good improvement.
Good job
Thanks so much!
A quick question - IMO ArgumentListMatcher and associated code could use a little refactoring to better express intent, and to include blocks in the args verification in a more consistent way (that's one of the reasons I stopped at a_block). I'm happy to attempt this if it makes sense to do so, and of course, it goes without saying that I'll keep the changes small, and will completely understand if they are rejected. Would that be useful?
You are welcome to have a go at refactoring it if you think you can make it better, please make sure you understand the matcher protocol before changing method names, e.g. it's important and non optional that ===
is used as the match method, as it's part of our composability and is what lets argument matchers and other matchers be combined.
Thinking about this, theres now ambiguity as to what we mean when, @myronmarston I'm leaning towards adding a_block
to match a block "anywhere", and then adding .with_block
to mean the block argument in order to be ambiguous, meaning with(...)
s arguments will remain "real" argument. Effectively .with_block
would just check for the presence of &block
, or could check for a specific block?
Procs/Lambdas as an argument and blocks are different things. I suggest consider a_block
and a_proc
/a_lambda
/a_callable
?
@JonRowe yep, well noted, I will keep that in mind.
Yep agree that bound and unbound blocks be treated differently. I was unaware of and_yield
when I picked up this ticket, but +1 to @JonRowe's point IMO the contracts and use cases for with(a_block)
and_yield
and with(a_block, ..., a_block)
should be defined before proceeding further to avoid ambiguous apis. I will defer to your judgement, and am happy to attempt an implementation once the decision is made.
They're not different enough, and can be passed around... Consider:
2.5.1 :001 > def a(&block)
2.5.1 :002?> puts block.inspect
2.5.1 :003?> block
2.5.1 :004?> end
=> :a
2.5.1 :005 > a = a { |a| }
#<Proc:0x00007fd290a01440@(irb):4>
=> nil
2.5.1 :006 > b = a(&proc { |b| })
#<Proc:0x00007fd2909e1c58@(irb):5>
=> nil
2.5.1 :007 > c = a(&lambda { |c| })
#<Proc:0x00007fd2909d0548@(irb):6 (lambda)>
=> nil
2.5.1 :008 > Proc === a
=> true
2.5.1 :009 > Proc === b
=> true
2.5.1 :010 > Proc === c
=> true
@JonRowe @benoittgt @ioquatix Working toward treating args and blocks in a more similar manner, I've done some work to try to tease out ReceivedMessage
and ReceivedMessages
as concrete classes from Proxy
and delegate to them as a first step. Do let me know if this makes sense, or if I should move in a different direction or just drop it entirely. https://github.com/rspec/rspec-mocks/compare/master...kaiwren:refactor-extract-ReceivedMessage-from-Proxy
I'm not sure why this was never merged, migrated to the monorepo as rspec/rspec#114