Ben Mabey
2008-Apr-09 05:29 UTC
[rspec-users] What is the best way to verify that yield is called?
Hello all, What is the best way to verify that a method yields a block that is passed in? Before I added any ''yield'' to my method I created this spec: it "should yield a message_delivery object" do create_message_in_factory do |message_delivery| message_delivery.should be_instance_of(MessageDelivery) end end This passed without me modifying my method which is not what I was hoping for. So I tried this spec and it gave me red: it "should yield the given block" do @block_yieled = false create_message_in_factory do |message_delivery| @block_yieled = true end @block_yieled.should be_true end Seems kinda hackish, but it did work. Is this the best way to spec this or is there already a matcher for this? If there isn''t currently a matcher would be the above way be a good way to create a custom matcher? Thanks for your thoughts, Ben
Rick DeNatale
2008-Apr-09 12:50 UTC
[rspec-users] What is the best way to verify that yield is called?
On Wed, Apr 9, 2008 at 1:29 AM, Ben Mabey <ben at benmabey.com> wrote:> Hello all, > What is the best way to verify that a method yields a block that is > passed in? > > Before I added any ''yield'' to my method I created this spec: > > it "should yield a message_delivery object" do > create_message_in_factory do |message_delivery| > message_delivery.should be_instance_of(MessageDelivery) > end > end > > This passed without me modifying my method which is not what I was > hoping for. So I tried this spec and it gave me red: > > it "should yield the given block" do > @block_yieled = false > create_message_in_factory do |message_delivery| > @block_yieled = true > end > @block_yieled.should be_true > end > > > Seems kinda hackish, but it did work. Is this the best way to spec this > or is there already a matcher for this?Another approach might be: it "should yield the given block" do block_body = mock("block body") block_body.should_receive(:got_here) create_message_in_factory_do | message_delivery | block_body.got_here end end -- Rick DeNatale My blog on Ruby http://talklikeaduck.denhaven2.com/
Ben Mabey
2008-Apr-09 14:59 UTC
[rspec-users] What is the best way to verify that yield is called?
>> >> it "should yield the given block" do >> @block_yieled = false >> create_message_in_factory do |message_delivery| >> @block_yieled = true >> end >> @block_yieled.should be_true >> end >> >> >> Seems kinda hackish, but it did work. Is this the best way to spec this >> or is there already a matcher for this? >> > > Another approach might be: > > it "should yield the given block" do > block_body = mock("block body") > block_body.should_receive(:got_here) > create_message_in_factory_do | message_delivery | > block_body.got_here > end > end > > > > > >Thanks Rick! Both of these way work but I think they are both too ugly and should be created into a more expressive matcher. Given the above two options can anyone see an advantage to one way or the other? Or maybe another option is out there that is better? Thanks, Ben
David Chelimsky
2008-Apr-09 15:54 UTC
[rspec-users] What is the best way to verify that yield is called?
On Wed, Apr 9, 2008 at 10:59 AM, Ben Mabey <ben at benmabey.com> wrote:> > >> > >> it "should yield the given block" do > >> @block_yieled = false > >> create_message_in_factory do |message_delivery| > >> @block_yieled = true > >> end > >> @block_yieled.should be_true > >> end > >> > >> > >> Seems kinda hackish, but it did work. Is this the best way to spec this > >> or is there already a matcher for this? > >> > > > > Another approach might be: > > > > it "should yield the given block" do > > block_body = mock("block body") > > block_body.should_receive(:got_here) > > create_message_in_factory_do | message_delivery | > > block_body.got_here > > end > > end > > > > > > > > > > > > > Thanks Rick! > Both of these way work but I think they are both too ugly and should be > created into a more expressive matcher.What are you looking to specify and how do you envision the syntax? I''m thinking something like: obj.should yield_with(no_args).on(:message) def message yield end list.should yield_with(1).then(2).then(3).on(:each) def each yield 1 yield 2 yield 3 end That all make sense?> Given the above two options can anyone see an advantage to one way or > the other? Or maybe another option is out there that is better? > > Thanks, > Ben > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Rick DeNatale
2008-Apr-09 16:33 UTC
[rspec-users] What is the best way to verify that yield is called?
On Wed, Apr 9, 2008 at 11:54 AM, David Chelimsky <dchelimsky at gmail.com> wrote:> What are you looking to specify and how do you envision the syntax? > > I''m thinking something like: > > obj.should yield_with(no_args).on(:message) > > def message > yield > end > > list.should yield_with(1).then(2).then(3).on(:each) > > def each > yield 1 > yield 2 > yield 3 > end > > That all make sense?Sorta, but what about arguments to the message? def message(x, y, z) yield x+y+z end obj.receiving(:message).with(1,2,3).should yield(6) (1..3).receiving(:each_with_index).should yield_with([1,0]).then([2,1]).then([3,2]) or (1..3).receiving(:each_with_index).should yield_with([1,0],([2,1],[3,2]) (1..3).receiving(:inject).with(0).should yield_with ??????? Now it gets tricky since the sequence of yielded values depends on the block . I don''t know that I like where this is going. -- Rick DeNatale My blog on Ruby http://talklikeaduck.denhaven2.com/
David Chelimsky
2008-Apr-09 16:46 UTC
[rspec-users] What is the best way to verify that yield is called?
On Wed, Apr 9, 2008 at 12:33 PM, Rick DeNatale <rick.denatale at gmail.com> wrote:> On Wed, Apr 9, 2008 at 11:54 AM, David Chelimsky <dchelimsky at gmail.com> wrote: > > > What are you looking to specify and how do you envision the syntax? > > > > I''m thinking something like: > > > > obj.should yield_with(no_args).on(:message) > > > > def message > > yield > > end > > > > list.should yield_with(1).then(2).then(3).on(:each) > > > > def each > > yield 1 > > yield 2 > > yield 3 > > end > > > > That all make sense? > > Sorta, but what about arguments to the message? > > def message(x, y, z) > yield x+y+z > end > > obj.receiving(:message).with(1,2,3).should yield(6) > > (1..3).receiving(:each_with_index).should > yield_with([1,0]).then([2,1]).then([3,2]) > > or > > (1..3).receiving(:each_with_index).should yield_with([1,0],([2,1],[3,2]) > > (1..3).receiving(:inject).with(0).should yield_with ??????? > > Now it gets tricky since the sequence of yielded values depends on the block . > > I don''t know that I like where this is going.Agreed, this could get hairy. Perhaps we should chalk this up to implementation detail? Looking back at the OP: it "should yield a message_delivery object" do create_message_in_factory do |message_delivery| message_delivery.should be_instance_of(MessageDelivery) end end How about something more like this as an idiom: it "should yield a message_delivery object" do create_message_in_factory do |message_delivery| return message_delivery end.should be_instance_of(MessageDelivery) end This still uses the contents of the blog to set an expectation, but is perhaps more expressive about the fact that we''re not really interested in the contents of the block as much as we are what the end result is. WDYT?
Ben Mabey
2008-Apr-09 18:01 UTC
[rspec-users] What is the best way to verify that yield is called?
Rick DeNatale wrote:> On Wed, Apr 9, 2008 at 11:54 AM, David Chelimsky <dchelimsky at gmail.com> wrote: > > >> What are you looking to specify and how do you envision the syntax? >> >> I''m thinking something like: >> >> obj.should yield_with(no_args).on(:message) >> >> def message >> yield >> end >> >> list.should yield_with(1).then(2).then(3).on(:each) >> >> def each >> yield 1 >> yield 2 >> yield 3 >> end >>Hmmm.. I didn''t think about multiple yields...>> That all make sense? >> > > Sorta, but what about arguments to the message? > > def message(x, y, z) > yield x+y+z > end > > obj.receiving(:message).with(1,2,3).should yield(6) >Or maybe: obj.when_sent(:message, 1,2,3).should yield(6) I don''t know if the extra ''with'' layer is needed since the sending of messages to objects is a very commonly used idiom in ruby. To keep the rspec syntax consistent your suggestion may be better so I could see it either way.> (1..3).receiving(:each_with_index).should > yield_with([1,0]).then([2,1]).then([3,2]) > > or > > (1..3).receiving(:each_with_index).should yield_with([1,0], [2,1],[3,2]) >The multiple args in the yield/yield_with call is more consistent with the way rspec''s mocking of return values work (i.e. mock.shou..and_return([1,2],[0,4]) ) than the ''then'' syntax suggested above. I guess I could go either way on this one as well.> (1..3).receiving(:inject).with(0).should yield_with ??????? > > Now it gets tricky since the sequence of yielded values depends on the block . > > I don''t know that I like where this is going. > >My original goal was only to ease the pain on my eyes for the the simple cases. I never considered the more involved cases, so you make a good point. I can''t think of an eloquent way of handling this. I don''t think a matcher could ever handle all of the cases. Hmmm... -Ben
Ben Mabey
2008-Apr-09 18:15 UTC
[rspec-users] What is the best way to verify that yield is called?
David Chelimsky wrote:> On Wed, Apr 9, 2008 at 12:33 PM, Rick DeNatale <rick.denatale at gmail.com> wrote: > >> On Wed, Apr 9, 2008 at 11:54 AM, David Chelimsky <dchelimsky at gmail.com> wrote: >> >> > What are you looking to specify and how do you envision the syntax? >> > >> > I''m thinking something like: >> > >> > obj.should yield_with(no_args).on(:message) >> > >> > def message >> > yield >> > end >> > >> > list.should yield_with(1).then(2).then(3).on(:each) >> > >> > def each >> > yield 1 >> > yield 2 >> > yield 3 >> > end >> > >> > That all make sense? >> >> Sorta, but what about arguments to the message? >> >> def message(x, y, z) >> yield x+y+z >> end >> >> obj.receiving(:message).with(1,2,3).should yield(6) >> >> (1..3).receiving(:each_with_index).should >> yield_with([1,0]).then([2,1]).then([3,2]) >> >> or >> >> (1..3).receiving(:each_with_index).should yield_with([1,0],([2,1],[3,2]) >> >> (1..3).receiving(:inject).with(0).should yield_with ??????? >> >> Now it gets tricky since the sequence of yielded values depends on the block . >> >> I don''t know that I like where this is going. >> > > Agreed, this could get hairy. Perhaps we should chalk this up to > implementation detail? > > Looking back at the OP: > > it "should yield a message_delivery object" do > create_message_in_factory do |message_delivery| > message_delivery.should be_instance_of(MessageDelivery) > end > end > > How about something more like this as an idiom: > > it "should yield a message_delivery object" do > create_message_in_factory do |message_delivery| > return message_delivery > end.should be_instance_of(MessageDelivery) > end >That makes sense for the cases when an object is yielded but what would you do if no object is yielded and the block is just suppose to execute? I guess you could do: it "should yield the given block" do some_method do return 42 end.should == 42 end But this does not look any better than the two previously suggested ways IMO.> This still uses the contents of the blog to set an expectation, but is > perhaps more expressive about the fact that we''re not really > interested in the contents of the block as much as we are what the end > result is. > > WDYT? >In the case where the an object is yielded I do like you suggestion because it covers everything. I just don''t know about when no object is yielded. I guess if a matcher is not possible I will have to settle for one of the approaches above when no object is yielded. Any more thoughts on the subject? Thanks, Ben