Shot (Piotr Szotkowski)
2009-Feb-28 21:29 UTC
[rspec-users] Rspecing an enumerator from outside-in woes
I?m trying to spec a system from outside-in as an excercise in ?emergent design? ? and so far I love it; it made me think about the design for two days before I even wrote the first spec? :) My most-outside class seems to be a prime candidate for an #each method. The attached (stripped) spec carrying two of the approaches I came up with passes, and raises the following quesitions ? any answer would be most appreciated! 1. A philosophical/kosherness question: In the finished system Decomposer#each will yield Decomposition objects, but as I?m specing from outside-in, the Decomposition class is not yet created. In the attached example I?m using an Array as a poor man?s Decomposition replacement. Is this a sane approach, or should I rather create a skeletal Decomposition#initialize instead? 2. The first spec shows my initial approach, with a variable polling the stuff yielded by #each and then validating its contents, but it seems clumsy? 3. ?so I came up with the second, Decomposer.new.to_enum approach, which simply validates the enumrator?s #next objects. Unfortunately, this does not seem to trigger #should_receive(:each) on the *_gen mocks and made me #stub!(:each) on them instead. Is this because I?m using RSpec 1.1.12 with Ruby 1.9.1, or am I missing something on how message expectations work with lazy iterators (and, thus, #should_receive fail properly)? 4. Is there any better or more elegant way to spec an #each (or #next) method? ? Shot -- uo???n??suo? ??pun -------------- next part -------------- A non-text attachment was scrubbed... Name: decomposer_spec.rb Type: application/x-ruby Size: 2147 bytes Desc: not available URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20090228/30ac822e/attachment.bin> -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 197 bytes Desc: Digital signature URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20090228/30ac822e/attachment-0001.bin>
Shot (Piotr Szotkowski) wrote:> I?m trying to spec a system from outside-in as an excercise in ?emergent > design? ? and so far I love it; it made me think about the design for > two days before I even wrote the first spec? :) > > My most-outside class seems to be a prime candidate for an #each method. > The attached (stripped) spec carrying two of the approaches I came up > with passes, and raises the following quesitions ? any answer would be > most appreciated! > > 1. A philosophical/kosherness question: In the finished system > Decomposer#each will yield Decomposition objects, but as I?m specing > from outside-in, the Decomposition class is not yet created. In the > attached example I?m using an Array as a poor man?s Decomposition > replacement. Is this a sane approach, or should I rather create > a skeletal Decomposition#initialize instead? > > 2. The first spec shows my initial approach, with a variable polling the > stuff yielded by #each and then validating its contents, but it seems > clumsy? > > 3. ?so I came up with the second, Decomposer.new.to_enum approach, which > simply validates the enumrator?s #next objects. Unfortunately, this does > not seem to trigger #should_receive(:each) on the *_gen mocks and made > me #stub!(:each) on them instead. Is this because I?m using RSpec 1.1.12 > with Ruby 1.9.1, or am I missing something on how message expectations > work with lazy iterators (and, thus, #should_receive fail properly)? > > 4. Is there any better or more elegant > way to spec an #each (or #next) method? >Hmm.. maybe this is what you are looking for (From http://rspec.info/documentation/mocks/message_expectations.html): Yielding my_mock.should_receive(:msg).once.and_yield(<value-1>, <value-2>, ..., <value-n>) When the expected message is received, the mock will yield the values to the passed block. To mock a method which yields values multiple times, and_yield can be chained. my_mock.should_receive(:msg).once.and_yield(<value-0-1>, <value-0-2>, ..., <value-0-n>). and_yield(<value-1-1>, <value-1-2>, ..., <value-1-n>). and_yield(<value-2-1>, <value-2-2>, ..., <value-2-n>) HTH, Ben
Shot (Piotr Szotkowski)
2009-Mar-01 03:18 UTC
[rspec-users] Rspecing an enumerator from outside-in woes
Ben Mabey:> Hmm.. maybe this is what you are looking for (From > http://rspec.info/documentation/mocks/message_expectations.html):Thanks for your interest and the pointer, but my questions weren?t about how to make my mocks yield to the code that calls them ? if you look at the code I attached previously you?ll see I already do that. :) The questions were (among other things) about (a) how to elegantly spec a method that is supposed to yield and (b) whether the #to_enum/#next combo not triggering #should_receive expectations is a known bug of RSpec 1.1.12 (under Ruby 1.9.1). ? Shot -- uo???n??suo? ??pun -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 197 bytes Desc: Digital signature URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20090301/820d574a/attachment.bin>
Shot (Piotr Szotkowski) wrote:> Ben Mabey: > > >> Hmm.. maybe this is what you are looking for (From >> http://rspec.info/documentation/mocks/message_expectations.html): >> > > Thanks for your interest and the pointer, but my questions weren?t about > how to make my mocks yield to the code that calls them ? if you look at > the code I attached previously you?ll see I already do that. :) >Ahh, sorry. I didn''t see you had attached code. If I get a chance I''ll take a closer look later. -Ben> The questions were (among other things) about (a) how to elegantly spec > a method that is supposed to yield and (b) whether the #to_enum/#next > combo not triggering #should_receive expectations is a known bug of > RSpec 1.1.12 (under Ruby 1.9.1). > > ? Shot > > ------------------------------------------------------------------------ > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
Matt Wynne
2009-Mar-01 13:27 UTC
[rspec-users] Rspecing an enumerator from outside-in woes
On 28 Feb 2009, at 21:29, Shot (Piotr Szotkowski) wrote:> I?m trying to spec a system from outside-in as an excercise in > ?emergent > design? ? and so far I love it; it made me think about the design for > two days before I even wrote the first spec? :)Great :)> My most-outside class seems to be a prime candidate for an #each > method. > The attached (stripped) spec carrying two of the approaches I came up > with passes, and raises the following quesitions ? any answer would be > most appreciated! > > 1. A philosophical/kosherness question: In the finished system > Decomposer#each will yield Decomposition objects, but as I?m specing > from outside-in, the Decomposition class is not yet created. In the > attached example I?m using an Array as a poor man?s Decomposition > replacement. Is this a sane approach, or should I rather create > a skeletal Decomposition#initialize instead?I think you should try to write the specs so they won''t have to change when you build the real implementation. That doesn''t mean creating an actual Decomposition class just yet, but it does mean that you should return something that looks enough like one for the tests to still be valid when you swap one in.> 2. The first spec shows my initial approach, with a variable polling > the > stuff yielded by #each and then validating its contents, but it seems > clumsy?I think what you''re finding clumsy here is the mocking setup. You don''t always have to use mock objects as your ''test doubles'' and often it''s much easier (and leaves behind more flexible tests) if you use stubs rather than mocks. In this case, although it''s possible to create a test double for the Generators using RSpec''s mocking framework, it''s pretty awkward and hard to read. I would be tempted to write my own test double instead. Applying outside-in to the behaviour I want from the test double, I''d write this: http://gist.github.com/72327 I''ll leave it to you to code the #each method on the FakeGenerator, but hopefully you get the idea. Notice that I just used an Array as the fake for @uv_gen - I might have missed something but I think that would provide an adequate double for the behaviour you had specified for the mock.> 3. ?so I came up with the second, Decomposer.new.to_enum approach, > which > simply validates the enumrator?s #next objects. Unfortunately, this > does > not seem to trigger #should_receive(:each) on the *_gen mocks and made > me #stub!(:each) on them instead. Is this because I?m using RSpec > 1.1.12 > with Ruby 1.9.1, or am I missing something on how message expectations > work with lazy iterators (and, thus, #should_receive fail properly)?I think you''re getting too far into specifying the implementation here. I like the #each approach better. Hope that helps. Matt Wynne http://blog.mattwynne.net http://www.songkick.com
Shot (Piotr Szotkowski)
2009-Mar-01 17:30 UTC
[rspec-users] Rspecing an enumerator from outside-in woes
Thanks a lot, Matt, for your reply! It?s seriously most enlightening. Matt Wynne:> On 28 Feb 2009, at 21:29, Shot (Piotr Szotkowski) wrote:>> 1. A philosophical/kosherness question: In the finished system >> Decomposer#each will yield Decomposition objects, but as I?m specing >> from outside-in, the Decomposition class is not yet created. In the >> attached example I?m using an Array as a poor man?s Decomposition >> replacement. Is this a sane approach, or should I rather create >> a skeletal Decomposition#initialize instead?> I think you should try to write the specs so they won''t have to change > when you build the real implementation. That doesn''t mean creating an > actual Decomposition class just yet, but it does mean that you should > return something that looks enough like one for the tests to still be > valid when you swap one in.Hmm, interesting ? so an outside-in implementation should side-step using future classes? constructors, and the implementation code should actually change when the relevant classes appear? I ended up creating a skeletal Decomposition class, but then had to add Decomposition#==(other) ? which, in turn, made me add attribute accessors ? just to be able to test against Decomposition objects in RSpec. Decomposition#== will be useful in the future, but currently it exists solely so I can use RSpec?s ?.should == Decomposition.new(?), which seems wrong. Hmm, another thing to ponder upon ? every time a new RSpec paradigm shows me something new, some other, unrelated spec begins to raise suspicions? :)> I think what you''re finding clumsy here is the mocking setup. You > don''t always have to use mock objects as your ''test doubles'' and often > it''s much easier (and leaves behind more flexible tests) if you use > stubs rather than mocks.Thanks a ton for the Array-based generators ? I haven?t thought of that; they are most interesting. I can?t use your example verbatim, as in my real code Decomposer.new takes class names and only then instantiates the relevant generators?, but it surely opened my eyes on stubbing objects with general-purpose classes rather than mocking them. I?ll see how I can use them to clean-up the specs. :) ? http://github.com/Chastell/art-decomp/commit/f9f8d3b2a3e431290d0656f7244b64f5376fab8f>> 3. ?so I came up with the second, Decomposer.new.to_enum approach, >> which simply validates the enumrator?s #next objects. Unfortunately, >> this does not seem to trigger #should_receive(:each) on the *_gen >> mocks and made me #stub!(:each) on them instead. Is this because >> I?m using RSpec 1.1.12 with Ruby 1.9.1, or am I missing something >> on how message expectations work with lazy iterators (and, thus, >> #should_receive fail properly)?> I think you''re getting too far into specifying the > implementation here. I like the #each approach better.I think I agree ? but the real question was why don?t the (lazy) enumerator?s #next calls satisfy the mocks? #should_receive()s ? am I missing something fundamental, or is this simply an RSpec 1.1.12 incompatibility with Ruby 1.9.1? For reference, my original attachment: http://gist.github.com/72399 ? if you replace the #stub!()s in the second spec with #should_receive()s, the spec breaks with (allegedly) unsatisfied expectations. ? Shot -- uo???n??suo? ??pun -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 197 bytes Desc: Digital signature URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20090301/b8481eb4/attachment.bin>
Matt Wynne
2009-Mar-01 18:53 UTC
[rspec-users] Rspecing an enumerator from outside-in woes
On 1 Mar 2009, at 17:30, Shot (Piotr Szotkowski) wrote:> Thanks a lot, Matt, for your reply! It?s seriously most enlightening. > > Matt Wynne: > >> On 28 Feb 2009, at 21:29, Shot (Piotr Szotkowski) wrote: > >>> 1. A philosophical/kosherness question: In the finished system >>> Decomposer#each will yield Decomposition objects, but as I?m specing >>> from outside-in, the Decomposition class is not yet created. In the >>> attached example I?m using an Array as a poor man?s Decomposition >>> replacement. Is this a sane approach, or should I rather create >>> a skeletal Decomposition#initialize instead? > >> I think you should try to write the specs so they won''t have to >> change >> when you build the real implementation. That doesn''t mean creating an >> actual Decomposition class just yet, but it does mean that you should >> return something that looks enough like one for the tests to still be >> valid when you swap one in. > > Hmm, interesting ? so an outside-in implementation should side-step > using future classes? constructors, and the implementation code should > actually change when the relevant classes appear?I wouldn''t suggest you did anything that meant you had to change the implementation later when you replace the fake with a real object. Assuming you wanted to keep your focus on the Decomposer class, but you knew that the concept of a Decomposition was a firm one in your design, you could define an empty Decomposition class, then stub the constructor to return a test double of some sort: class Decomposition end ... Decomposition.stub!(:new).and_return mock(''Decomposition'', :foo => ''bar'')> I ended up creating a skeletal Decomposition class, but then had to > add Decomposition#==(other) ? which, in turn, made me add attribute > accessors ? just to be able to test against Decomposition objects in > RSpec. > > Decomposition#== will be useful in the future, but currently it exists > solely so I can use RSpec?s ?.should == Decomposition.new(?), which > seems wrong. Hmm, another thing to ponder upon ? every time a new > RSpec > paradigm shows me something new, some other, unrelated spec begins to > raise suspicions? :)I''m less fussy these days about adding a bit of code to make something testable - I think of it a bit like adding screws to let you take an appliance like a CD player apart, rather than sealing it all up with glue. Having said that, if you have to work hard to do this, there might be a smell in your design. You could consider the Decomposition with all those nasty getters all over it to be something you only use in your tests - a TestableDecomposition. One technique for creating a test double is to subclass the object you want to fake, and add extra behaviour in the subclass that make the object more testable, without adding its behaviour. Exposing some state in the form of getters, or adding #== might be an example of this. Ideally though, you really want to avoid testing state and instead think about testing the interactions between objects. If the relationship between the Decomposer and the Decomposition is for one to create instances of the other, then I would be quite satisfied writing mock expectations on the Decomposition''s constructor, like this: Decomposition.should_receive(:new).with [1,2]>> I think what you''re finding clumsy here is the mocking setup. You >> don''t always have to use mock objects as your ''test doubles'' and >> often >> it''s much easier (and leaves behind more flexible tests) if you use >> stubs rather than mocks. > > Thanks a ton for the Array-based generators ? I haven?t thought of > that; > they are most interesting. I can?t use your example verbatim, as in my > real code Decomposer.new takes class names and only then instantiates > the relevant generators?, but it surely opened my eyes on stubbing > objects with general-purpose classes rather than mocking them. I?ll > see how I can use them to clean-up the specs. :) > > ? http://github.com/Chastell/art-decomp/commit/f9f8d3b2a3e431290d0656f7244b64f5376fab8f > >>> 3. ?so I came up with the second, Decomposer.new.to_enum approach, >>> which simply validates the enumrator?s #next objects. Unfortunately, >>> this does not seem to trigger #should_receive(:each) on the *_gen >>> mocks and made me #stub!(:each) on them instead. Is this because >>> I?m using RSpec 1.1.12 with Ruby 1.9.1, or am I missing something >>> on how message expectations work with lazy iterators (and, thus, >>> #should_receive fail properly)? > >> I think you''re getting too far into specifying the >> implementation here. I like the #each approach better. > > I think I agree ? but the real question was why don?t the (lazy) > enumerator?s #next calls satisfy the mocks? #should_receive()s ? > am I missing something fundamental, or is this simply an RSpec 1.1.12 > incompatibility with Ruby 1.9.1? > > For reference, my original attachment: http://gist.github.com/72399 ? > if you replace the #stub!()s in the second spec with > #should_receive()s, > the spec breaks with (allegedly) unsatisfied expectations.Sorry, not sure about that one - I''ve not tried playing with these lazy enumerators - is this a Ruby 1.9 thing Matt Wynne http://blog.mattwynne.net http://www.songkick.com
Shot (Piotr Szotkowski)
2009-Mar-02 23:32 UTC
[rspec-users] Rspecing an enumerator from outside-in woes
Once again ? thanks a ton for your response! I wish ?The RSpec Book? answered my use cases; I bought it hoping I?ll learn all this stuff from it, but it seems the most interesting chapters are not written yet. :) Matt Wynne:> Assuming you wanted to keep your focus on the Decomposer class, but > you knew that the concept of a Decomposition was a firm one in your > design, you could define an empty Decomposition class, then stub the > constructor to return a test double of some sort:> class Decomposition > end> ...> Decomposition.stub!(:new).and_return mock(''Decomposition'', :foo => ''bar'')Ah, a very good point. :) The idea of subclassing something to make it testable is also interesting; I?d rather try hard to come up with a better spec and/or design, but it?s good to remember there?s a solution to keep all the code added solely for testability in one place.> Ideally though, you really want to avoid testing state and instead > think about testing the interactions between objects. If the > relationship between the Decomposer and the Decomposition is for > one to create instances of the other, then I would be quite satisfied > writing mock expectations on the Decomposition''s constructor, like > this:> Decomposition.should_receive(:new).with [1,2]And this is exactly where you hit the nail on the head. :) I think my main problem at this stage of ?getting RSpec? is the (shrinking, but still existing) inability to blur the border between the RSpec world and the world of the code under test. I happily started to use mocks and stubs, but I keep forgetting that they can be applied to the stuff from the ?other? world; I hope I?ll keep that in mind from now on. FWIW, your example on replacing ?.should_receive(:each).and_yield(?).and_yield(?).? with a fake generator struck a very nice chord with me, and I managed to side-step the problem that all three of my generator types expect a varying number of params to their #each calls: http://github.com/Chastell/art-decomp/commit/9979e3b687a33fd75e6394b6297e2200b052b354> On 1 Mar 2009, at 17:30, Shot (Piotr Szotkowski) wrote:>> I think I agree ? but the real question was why don?t the (lazy) >> enumerator?s #next calls satisfy the mocks? #should_receive()s ? >> am I missing something fundamental, or is this simply an RSpec 1.1.12 >> incompatibility with Ruby 1.9.1?> Sorry, not sure about that one - I''ve not tried playing > with these lazy enumerators - is this a Ruby 1.9 thingYes and no ? some/all of the Enumerator stuff was backported to 1.8 and is included in Ruby 1.8.7. Basically, if you have an #each method on your class, you can create an Enumerator? for its objects and then do very nice things, like having a free #with_index traversal or fetching the elements one-by-one with #next. The latter is useful for classes generating/representing infinite sequences and in cases (like my case of interdependent generators) when the cost of generating subsequent elements is rather high and it would be wasteful to generate all of them upfront. ? http://ruby-doc.org/core-1.9/classes/Enumerable/Enumerator.html ? Shot -- uo???n??suo? ??pun -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 197 bytes Desc: Digital signature URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20090303/5b5e72e2/attachment.bin>
Matt Wynne
2009-Mar-02 23:44 UTC
[rspec-users] Rspecing an enumerator from outside-in woes
On 2 Mar 2009, at 23:32, Shot (Piotr Szotkowski) wrote:> Once again ? thanks a ton for your response! I wish ?The RSpec Book? > answered my use cases; I bought it hoping I?ll learn all this stuff > from > it, but it seems the most interesting chapters are not written yet. :) > > Matt Wynne: > >> Assuming you wanted to keep your focus on the Decomposer class, but >> you knew that the concept of a Decomposition was a firm one in your >> design, you could define an empty Decomposition class, then stub the >> constructor to return a test double of some sort: > >> class Decomposition >> end > >> ... > >> Decomposition.stub!(:new).and_return mock(''Decomposition'', :foo >> => ''bar'') > > Ah, a very good point. :) The idea of subclassing something to make > it testable is also interesting; I?d rather try hard to come up > with a better spec and/or design, but it?s good to remember there?s > a solution to keep all the code added solely for testability in one > place.The idea comes from this book: http://www.amazon.co.uk/xUnit-Test-Patterns-Refactoring-Signature There are lots of other good ideas in that book. Some of them are only necessary because of the awkward things statically-typed languages force you to do, but many of them are equally applicable to and useful in Ruby.>> Ideally though, you really want to avoid testing state and instead >> think about testing the interactions between objects. If the >> relationship between the Decomposer and the Decomposition is for >> one to create instances of the other, then I would be quite satisfied >> writing mock expectations on the Decomposition''s constructor, like >> this: > >> Decomposition.should_receive(:new).with [1,2] > > And this is exactly where you hit the nail on the head. :) I think > my main problem at this stage of ?getting RSpec? is the (shrinking, > but still existing) inability to blur the border between the RSpec > world > and the world of the code under test. I happily started to use mocks > and > stubs, but I keep forgetting that they can be applied to the stuff > from > the ?other? world; I hope I?ll keep that in mind from now on. > > FWIW, your example on replacing > ?.should_receive(:each).and_yield(?).and_yield(?).? > with a fake generator struck a very nice chord with > me, and I managed to side-step the problem that all > three of my generator types expect a varying number > of params to their #each calls: > http://github.com/Chastell/art-decomp/commit/9979e3b687a33fd75e6394b6297e2200b052b354Glad it helped :) Matt Wynne http://blog.mattwynne.net http://www.songkick.com