I''ve just been tying my brain in knots looking at bug #8687 ( http://rubyforge.org/tracker/index.php?func=detail&aid=8687&group_id=1917&atid=7477 ). I''ve been (1) trying to work out whether there is anything logically wrong with Mocha''s existing behaviour and (2) whether Mocha should support the requested functionality. It all centres around the use of the Expectation#yields method. I''ve put a couple of examples together to try and help me think about it. The first one (http://pastie.caboo.se/99412) is a simplified version of the example in the bug report using a block and stubbing a method that yields to that block. In the second example (http://pastie.caboo.se/99413) I tried to think about how it would work if I converted the block to a method on an object. I slightly ran out of steam on this one when I realised that in this case when I realised Executor#execute wouldn''t be called because the implementation of Collaborator#yielding_method is stubbed. This made me wonder if the Expectation#yields method should exist at all! In particular, in the block example, it feels odd because you are going from real code (ClassUnderTest) into mock code (mocked version of Collaborator) and back into real code (the block itself). I wonder if this is really sensible and/or useful. Has anybody got any thoughts? Thanks. -- James. http://blog.floehopper.org http://tumble.floehopper.org
It''s certainly necessary to test the code tucked away into these blocks, but I feel like yielding is testing in the wrong direction. Instead of yielding values or mocks into the block, perhaps we can check metadata about the provided block (arity, required params) and then test the functionality of the block explicitly. class Test::Unit::TestCase def test_up_them_all upcasing_block = lambda { |e| e.upcase } list_of_words = [ ''socks'', ''mittens'', ''flashlight'' ] list_of_words.expects(:map).with_block(upcasing_block) u = Upperizer.new u.up_them_all(list_of_words) assert_equal ''HONK'', upcasing_block(''honk'') end end On 9/21/07, James Mead <jamesmead44 at gmail.com> wrote:> I''ve just been tying my brain in knots looking at bug #8687 ( > http://rubyforge.org/tracker/index.php?func=detail&aid=8687&group_id=1917&atid=7477 > ). > > I''ve been (1) trying to work out whether there is anything logically wrong > with Mocha''s existing behaviour and (2) whether Mocha should support the > requested functionality. > > It all centres around the use of the Expectation#yields method. I''ve put a > couple of examples together to try and help me think about it. The first one > (http://pastie.caboo.se/99412) is a simplified version of the example in the > bug report using a block and stubbing a method that yields to that block. > > In the second example (http://pastie.caboo.se/99413) I tried to think about > how it would work if I converted the block to a method on an object. I > slightly ran out of steam on this one when I realised that in this case when > I realised Executor#execute wouldn''t be called because the implementation of > Collaborator#yielding_method is stubbed. > > This made me wonder if the Expectation#yields method should exist at all! In > particular, in the block example, it feels odd because you are going from > real code (ClassUnderTest) into mock code (mocked version of Collaborator) > and back into real code (the block itself). I wonder if this is really > sensible and/or useful. > > Has anybody got any thoughts? > > Thanks. > -- > James. > http://blog.floehopper.org > http://tumble.floehopper.org > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer >
I would weigh in that something like yields is needed and possibly the suggested patches as well. I do agree that it seems to work the wrong way. I went and looked at how I had used yields and found what I think validates my assertion above. Take for example: IO.open, IO.popen, etc. puts IO.popen("ls", "r") { |pipe| pipe.read } OK, so this example is contrived; However, I do think it points out some important things. It is desirable to have expectations of both the call to popen, or the block, or both; However, rather than yield as a modifier to the methods expectations, separate the block''s expectations, and return values out. Basically rather than one statement combining the two, require two statements. James Mead wrote:> I''ve just been tying my brain in knots looking at bug #8687 ( > http://rubyforge.org/tracker/index.php?func=detail&aid=8687&group_id=1917&atid=7477 > ). > > I''ve been (1) trying to work out whether there is anything logically wrong > with Mocha''s existing behaviour and (2) whether Mocha should support the > requested functionality. > > It all centres around the use of the Expectation#yields method. I''ve put a > couple of examples together to try and help me think about it. The first one > (http://pastie.caboo.se/99412) is a simplified version of the example in the > bug report using a block and stubbing a method that yields to that block. > > In the second example (http://pastie.caboo.se/99413) I tried to think about > how it would work if I converted the block to a method on an object. I > slightly ran out of steam on this one when I realised that in this case when > I realised Executor#execute wouldn''t be called because the implementation of > Collaborator#yielding_method is stubbed. > > This made me wonder if the Expectation#yields method should exist at all! In > particular, in the block example, it feels odd because you are going from > real code (ClassUnderTest) into mock code (mocked version of Collaborator) > and back into real code (the block itself). I wonder if this is really > sensible and/or useful. > > Has anybody got any thoughts? > > Thanks.
On 21/09/2007, Duncan Beevers <duncanbeevers at gmail.com> wrote:> It''s certainly necessary to test the code tucked away into these > blocks, but I feel like yielding is testing in the wrong direction. > > Instead of yielding values or mocks into the block, perhaps we can > check metadata about the provided block (arity, required params) and > then test the functionality of the block explicitly. > > class Test::Unit::TestCase > def test_up_them_all > upcasing_block = lambda { |e| e.upcase } > > list_of_words = [ ''socks'', ''mittens'', ''flashlight'' ] > list_of_words.expects(:map).with_block(upcasing_block) > > u = Upperizer.new > u.up_them_all(list_of_words) > > assert_equal ''HONK'', upcasing_block(''honk'') > end > end > > > On 9/21/07, James Mead <jamesmead44 at gmail.com> wrote: > > I''ve just been tying my brain in knots looking at bug #8687 ( > > http://rubyforge.org/tracker/index.php?func=detail&aid=8687&group_id=1917&atid=7477 > > ). > > > > I''ve been (1) trying to work out whether there is anything logically wrong > > with Mocha''s existing behaviour and (2) whether Mocha should support the > > requested functionality. > > > > It all centres around the use of the Expectation#yields method. I''ve put a > > couple of examples together to try and help me think about it. The first one > > (http://pastie.caboo.se/99412) is a simplified version of the example in the > > bug report using a block and stubbing a method that yields to that block. > > > > In the second example (http://pastie.caboo.se/99413) I tried to think about > > how it would work if I converted the block to a method on an object. I > > slightly ran out of steam on this one when I realised that in this case when > > I realised Executor#execute wouldn''t be called because the implementation of > > Collaborator#yielding_method is stubbed. > > > > This made me wonder if the Expectation#yields method should exist at all! In > > particular, in the block example, it feels odd because you are going from > > real code (ClassUnderTest) into mock code (mocked version of Collaborator) > > and back into real code (the block itself). I wonder if this is really > > sensible and/or useful. > > > > Has anybody got any thoughts? > > > > Thanks. > > -- > > James. > > http://blog.floehopper.org > > http://tumble.floehopper.org > > _______________________________________________ > > mocha-developer mailing list > > mocha-developer at rubyforge.org > > http://rubyforge.org/mailman/listinfo/mocha-developer > > > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer >This is certainly the direction I was starting to think about. I think the thing that is not clear from your example is where the Upperizer gets its *real* upcasing_block from. Also is this upcasing_block the same instance as the one in the local variable in the test? Do you think it''s sufficient to only check the arity of the block? In which case you wouldn''t actually need to supply the upcasing_block in the with_block clause, you could just specify the block''s arity. Something along the lines of with_block_having_arity(1). What do you think? -- James. http://blog.floehopper.org http://tumble.floehopper.org
On 21/09/2007, John Pywtorak <jpywtora at calpoly.edu> wrote:> I would weigh in that something like yields is needed and possibly the > suggested patches as well. I do agree that it seems to work the wrong > way. I went and looked at how I had used yields and found what I think > validates my assertion above. > > Take for example: IO.open, IO.popen, etc. > > puts IO.popen("ls", "r") { |pipe| pipe.read } > > OK, so this example is contrived; However, I do think it points out some > important things. It is desirable to have expectations of both the call > to popen, or the block, or both; However, rather than yield as a > modifier to the methods expectations, separate the block''s expectations, > and return values out. Basically rather than one statement combining > the two, require two statements.Thanks for your interest, although I''m not sure I follow your reasoning. Could you give examples of actual tests and code under test and explain either how you use the existing features of Mocha or how you''d like it to work? Thanks. -- James. http://blog.floehopper.org http://tumble.floehopper.org
Yeah, the upcasing_block example should be two tests; one testing the behavior of the block and the other testing that up_them_all applies a block to list in a certain fashion. The upcasing_block created in the test is not the same as the one (speculatively) declared in the Upperizer. Checking arity is a great start, but perhaps we could capture the block in Upperizer and then do some checks on it? def test_up_them_all list_of_words = [ ''socks'', ''mittens'', ''flashlight'' ] upcasing_block = mock_block(''Upcasing Block'').arity(1) list_of_words.expects(:map).with_block(upperizing_block) u = Upperizer.new u.up_them_all(list_of_words) # Invoking up_them_all captures upcasing_block assert_equal ''HONK'', upcasing_block(''honk'') # Let''s test it in isolation end Still feels kind of weird to pull data out of a class this way, but anonymous lambdas are slippery and you gotta grab when you can! On Jan 2, 2008 7:15 AM, James Mead <jamesmead44 at gmail.com> wrote:> > On 21/09/2007, Duncan Beevers <duncanbeevers at gmail.com> wrote: > > It''s certainly necessary to test the code tucked away into these > > blocks, but I feel like yielding is testing in the wrong direction. > > > > Instead of yielding values or mocks into the block, perhaps we can > > check metadata about the provided block (arity, required params) and > > then test the functionality of the block explicitly. > > > > class Test::Unit::TestCase > > def test_up_them_all > > upcasing_block = lambda { |e| e.upcase } > > > > list_of_words = [ ''socks'', ''mittens'', ''flashlight'' ] > > list_of_words.expects(:map).with_block(upcasing_block) > > > > u = Upperizer.new > > u.up_them_all(list_of_words) > > > > assert_equal ''HONK'', upcasing_block(''honk'') > > end > > end > > > > > > On 9/21/07, James Mead <jamesmead44 at gmail.com> wrote: > > > I''ve just been tying my brain in knots looking at bug #8687 ( > > > http://rubyforge.org/tracker/index.php?func=detail&aid=8687&group_id=1917&atid=7477 > > > ). > > > > > > I''ve been (1) trying to work out whether there is anything logically wrong > > > with Mocha''s existing behaviour and (2) whether Mocha should support the > > > requested functionality. > > > > > > It all centres around the use of the Expectation#yields method. I''ve put a > > > couple of examples together to try and help me think about it. The first one > > > (http://pastie.caboo.se/99412) is a simplified version of the example in the > > > bug report using a block and stubbing a method that yields to that block. > > > > > > In the second example (http://pastie.caboo.se/99413) I tried to think about > > > how it would work if I converted the block to a method on an object. I > > > slightly ran out of steam on this one when I realised that in this case when > > > I realised Executor#execute wouldn''t be called because the implementation of > > > Collaborator#yielding_method is stubbed. > > > > > > This made me wonder if the Expectation#yields method should exist at all! In > > > particular, in the block example, it feels odd because you are going from > > > real code (ClassUnderTest) into mock code (mocked version of Collaborator) > > > and back into real code (the block itself). I wonder if this is really > > > sensible and/or useful. > > > > > > Has anybody got any thoughts? > > > > > > Thanks. > > > -- > > > James. > > > http://blog.floehopper.org > > > http://tumble.floehopper.org > > > _______________________________________________ > > > mocha-developer mailing list > > > mocha-developer at rubyforge.org > > > http://rubyforge.org/mailman/listinfo/mocha-developer > > > > > _______________________________________________ > > mocha-developer mailing list > > mocha-developer at rubyforge.org > > http://rubyforge.org/mailman/listinfo/mocha-developer > > > > This is certainly the direction I was starting to think about. I think > the thing that is not clear from your example is where the Upperizer > gets its *real* upcasing_block from. Also is this upcasing_block the > same instance as the one in the local variable in the test? > > Do you think it''s sufficient to only check the arity of the block? In > which case you wouldn''t actually need to supply the upcasing_block in > the with_block clause, you could just specify the block''s arity. > Something along the lines of with_block_having_arity(1). > > What do you think? > -- > > James. > http://blog.floehopper.org > http://tumble.floehopper.org > _______________________________________________ > mocha-developer mailing list > mocha-developer at rubyforge.org > http://rubyforge.org/mailman/listinfo/mocha-developer >