[rspec-users] Mocking: brittle specs and tight coupling?

Phlip phlip2005 at gmail.com
Sun Apr 12 20:23:45 EDT 2009

David Chelimsky wrote:

>> Another tip: To TDD a new feature, don't clone a high-level test which calls
>> code which calls code which calls the code you need to change.
> FWIW, what you propose is the exact opposite of BDD, which suggests we
> start at the highest levels and work our way in.

My current day-job's most important project has a test suite that suffered from 
abuse of that concept. The original team, without enough refactoring, were 
cloning and modifying very-high-level tests, making them longer, and using them 
to TDD new features into the bottom of the models layer. Don't do that.

> How can you know what the lowest level code is supposed to do before
> you have higher level code invoking it? You can certainly make good
> guesses, and they might end up being good choices in the end, but they
> tend to bind you to a pre-determined design.

That sounds like James Kanze's over-educated arguments against TDD:

> Phlip wrote:
>> James Kanze wrote:
>> > In particular, you can't
>> > write a single line of code (unit test or implementation) before
>> > you know what the function should do,
>> I didn't bring up TDD, but if you are curious enough about it
>> to keep asking these entry-level FAQs,
> I'm not asking anything.  I'm simply stating an easily
> established fact, which wishful thinking seems to cause some
> people to ignore.  Tests definitly have their role, but until
> you know what the code should do, you can't begin to write them.
> And until you've written something down in black and white, you
> don't know it.

 From there, he'll wander off into "too smart to just try it" bullshit...

> Your recommendation also starts with cloning a pre-existing example,
> so I'm assuming this is a very specific sort of situation, where you
> have a certain amount of code in place. What about when you are
> starting a project for the first time?

Programming bottom-up gives you decoupled lower layers. Top-down gives you a way 
to tunnel from a new View feature into the code that supports it. The goal is 
you _could_ start a new feature either way, and you get the benefits of both 

I thought of describing that specific tip while adding "any!" to assert_xhtml. 
It would have been too easy to start with the high-level tests:

   def test_anybang_is_magic
     assert_xhtml SAMPLE_LIST do
       ul.kalika do
         any! 'Billings report'

     assert_xhtml_flunk SAMPLE_LIST do
       without! do
         any! 'Billings report'

Some of my features indeed started there, and some of them indeed do not yet 
have low-level tests.

But the entire call stack below that also at least has tests on each layer - 
except the actual code which converts a tag like select! into the fragment of 
XPath which matches //select[]. Oh, and that code around it had grown a little 
long. So in this case, I started there, and refactored out the single line that 
needed the change:

   def translate_tag(element)
     element.name.sub(/\!$/, '')

Then I can TDD translate_tag directly:

   def test_any_element
     bhw = assemble_BeHtmlWith{ any :attribute => 'whatever' }
     element = bhw.builder.doc.root
     assert{ bhw.translate_tag(element) == 'any' }
     bhw = assemble_BeHtmlWith{ any! :attribute => 'whatever' }
     element = bhw.builder.doc.root
     assert{ bhw.translate_tag(element) == '*' }


   def translate_tag(element)
     if element.name == 'any!'
       element.name.sub(/\!$/, '')

Only then I wrote the high-level tests, and they passed.

Note that RSpec requires the constructor to BeHtmlWith to be a little ... 
fruity, so I wrapped it and its Builder stuff up into assemble_BeHtmlWith...


More information about the rspec-users mailing list