Rspecing an enumerator from outside-in woes


rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

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)?

  1. 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(, , …,
)
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(, ,
…, ).
and_yield(, , …, ).
and_yield(, , …, )

HTH,
Ben

Ben M.:

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. :slight_smile:

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

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

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… :slight_smile:

Great :slight_smile:

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.

  1. 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:

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.

  1. …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

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… :slight_smile:

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. :slight_smile:

¹

  1. …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: decomposer_spec.rb · GitHub –
if you replace the #stub!()s in the second spec with #should_receive()s,
the spec breaks with (allegedly) unsatisfied expectations.

— Shot

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. :slight_smile:

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. :slight_smile: 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. :slight_smile: 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:

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/Enumerator.html

— Shot

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… :slight_smile:
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: decomposer_spec.rb · GitHub
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

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

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:
switch the generator mocks into stubs of a new StubGenerator class · chastell/art-decomp@9979e3b · GitHub

Glad it helped :slight_smile:

Matt W.
http://blog.mattwynne.net