Forum: RSpec Rspecing an enumerator from outside-in woes

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Shot (Piotr S.) (Guest)
on 2009-03-01 00:01
(Received via mailing list)
_______________________________________________
rspec-users mailing list
removed_email_address@domain.invalid
http://rubyforge.org/mailman/listinfo/rspec-users
Ben M. (Guest)
on 2009-03-01 04:30
(Received via mailing list)
Shot (Piotr S.) wrote:
> Decomposer#each will yield Decomposition objects, but as I’m specing
> 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_expe...

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 S.) (Guest)
on 2009-03-01 05:46
(Received via mailing list)
Ben M.:

> Hmm.. maybe this is what you are looking for (From
> http://rspec.info/documentation/mocks/message_expe...

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
Ben M. (Guest)
on 2009-03-01 06:48
(Received via mailing list)
Shot (Piotr S.) wrote:
>
Ahh, sorry. I didn't see you had attached code. If I get a chance I'll
take a closer look later.
-Ben
Matt W. (Guest)
on 2009-03-01 15:48
(Received via mailing list)
On 28 Feb 2009, at 21:29, Shot (Piotr S.) 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 :)

> 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 W.
http://blog.mattwynne.net
http://www.songkick.com
Shot (Piotr S.) (Guest)
on 2009-03-01 19:32
(Received via mailing list)
Thanks a lot, Matt, for your reply! It’s seriously most enlightening.

Matt W.:

> On 28 Feb 2009, at 21:29, Shot (Piotr S.) 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/f9f8d...

>> 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
Matt W. (Guest)
on 2009-03-01 21:08
(Received via mailing list)
On 1 Mar 2009, at 17:30, Shot (Piotr S.) wrote:

>>> replacement. Is this a sane approach, or should I rather create
> 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')


> 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]

> the relevant generators¹, but it surely opened my eyes on stubbing
>>> on how message expectations work with lazy iterators (and, thus,
> 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 W.
http://blog.mattwynne.net
http://www.songkick.com
Shot (Piotr S.) (Guest)
on 2009-03-03 01:34
(Received via mailing list)
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 W.:

> 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/9979e...

> On 1 Mar 2009, at 17:30, Shot (Piotr S.) 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 thing

Yes 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/En...

— Shot
Matt W. (Guest)
on 2009-03-03 02:10
(Received via mailing list)
On 2 Mar 2009, at 23:32, Shot (Piotr S.) wrote:

>> constructor to return a test double of some sort:
> 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-Refact...

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.

> my main problem at this stage of ‘getting RSpec’ is the (shrinking,
> 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/9979e...

Glad it helped :)

Matt W.
http://blog.mattwynne.net
http://www.songkick.com
This topic is locked and can not be replied to.