[rspec-devel] should_yield (is it needed?)

Martin Emde martin.emde at gmail.com
Tue Dec 1 16:27:52 EST 2009

I was a bit unclear about which one I'm advocating. I can see many ways to
achieve this that would be clear in different circumstances and I haven't
really put my weight behind one yet.

David Chelimsky wrote:

> I don't see how we can fail this example:
> describe "foo" do
>   def foo; # do nothing; end
>   it "yields" do
>     foo &should_yield
>   end
> end
> In this case the proc returned by should_yield is never invoked, which
> means we have no way of hooking into RSpec to declare that it should be.
> Sort of a catch 22.

should_yield is first invoked with "&should_yield". The should_yield method
is called and returns an object responding to #to_proc. The & calls #to_proc
on our object which returns a block that calls our expected method when it's

Here's my implementation. I actually think it is pretty cool, whether or not
it's the "right" solution:


Now just some thoughts on syntax...

What a "should yield" expectation is really testing is an "anonymous
function" call. Particularly, we're asserting that #call is called on our
"block" object in some form (yield or #call). In ruby, our "anonymous
functions" are blocks. The implementation and usage is then very similar to
a should_receive. If I were to structure this like a should receive, I would
expect something like this.

# should_receive style implementation.
block = mock_block.should_receive_yield.with(1,2,3)
[1,2,3].method("args", &block)

This actually shows that we're making a block with expectations on it, and
then we're passing the block in a familiar format "&block" which most
rubyists will recognize. It could even be shortened by people who understand
what they're doing to something like this:

[1,2,3].method("args", &mock_block.should_receive_yield.with(1,2,3))

This opens up a really comfortable feeling language where you can even have
return values from the block in a way that makes sense.

# this block doubles the number passed in
block = mock_block.should_receive_yield.with(1,2,3).and_return(2,4,6)
[1,2,3].map(&block).should == [2,4,6]

My only concern for the & syntax is just how confusing it is to most people.
This is really just stepping back a bit to expose my implementation a bit
more. In my implementation the "mock_block.should_recieve_yield" is just
"should_yield" which does the mock_block part behind the scenes. Thoughts?


P.S. Peter, your implementation reads well but the method(args) will call
the method right away without a block. That's the catch here that makes this
a tricky thing to spec.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://rubyforge.org/pipermail/rspec-devel/attachments/20091201/089c89d2/attachment.html>

More information about the rspec-devel mailing list