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

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

Reply via email to