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

David Chelimsky dchelimsky at gmail.com
Tue Dec 1 07:55:26 EST 2009

On Sun, Nov 29, 2009 at 3:11 PM, Martin Emde <martin.emde at gmail.com> wrote:

> On Tue, Nov 10, 2009 at 5:00 PM, David Chelimsky <dchelimsky at gmail.com>wrote:
>> [1,2,3].should yield_on(:each) ???
> This does follow rspec syntax more closely... Let's run through a few use
> cases I can imagine.
> current_user.stub!(:admin?).and_return(true)
> helper.should yield_on(:admin_only)
> # helper.admin_only &should_yield
> [1,2,3].should yield_on(:each).each_of(1,2,3)
> # [1,2,3].each &should_yield_each(1,2,3)
> h = { :a => 1, :b => 2 }
> h.should yield_on(:each).each_of(*h.to_a)
> h.each &should_yield_each(*h.to_a)
> helper.should yield_on(:link_to_function, "link
> text").with(an_instance_of(JavaScriptHelper::JavaScriptBuilder))
> #  helper.link_to_function "link text",
> &should_yield_with(an_instance_of(JavaScriptHelper::JavaScriptBuilder))
> I think both are pretty clear until the last example when this part in my
> format:  *"link text", &should_yield(...)*   starts to confuse what's
> going on exactly. It very clearly breaks from rspec syntax in that example.
> helper.link_to_functio("link text", &assert_yielded_block)
> That actually makes a bit more sense since the intention is clear. I'm
> passing a block that asserts that it will be yielded. However, this isn't
> rspec at all written like that.
> The yield_on format seems to specify "When I call this method on the
> receiver, I expect it to yield." This is very similar to user.should
> be_an_admin in that the actual method call is not being called on the
> receiver but passed into the matcher.
> Is this something we'd like to add to rspec? If so, I'll work out a patch.
> Martin

I'm a bit confused as to which syntax you're actually advocating now :)

One thing I'm realizing after your suggestion that "the actual method call
is not being called on the receiver" is that this is more akin to a message
expectation (i.e. mock) rather than a state-based expectation (should xxx).
That leads me to think this should be part of the mock framework rather than
the matcher framework, which leads me to think differently about syntax.

Also, from an implementation standpoint, I don't see how we can fail this

describe "foo" do
  def foo; # do nothing; end

  it "yields" do
    foo &should_yield

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. We might need something like this:

foo should_yield

The should_yield() method, in this case, can set the message expectation on
some sort of hidden proxy object and then return a proc that will record the
expectation as satisfied when it's called. But again, this is new syntax,
which I'm not sure I want to introduce for one concept.

Here's another alternative:

expect { foo }.to yield_to_block
expect { [1,2,3].each }.to yield_each_of(1,2,3)

This relates to a family of matchers that lie somewhere between state-based
matchers and mocks (raise_error, change), so it's a more familiar construct.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://rubyforge.org/pipermail/rspec-devel/attachments/20091201/332b5941/attachment.html>

More information about the rspec-devel mailing list