[rspec-users] Rspecing an enumerator from outside-in woes

Matt Wynne matt at mattwynne.net
Sun Mar 1 13:53:38 EST 2009


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



More information about the rspec-users mailing list