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

Zach Dennis zach.dennis at gmail.com
Sun Apr 12 22:36:50 EDT 2009


On Sun, Apr 12, 2009 at 8:23 PM, Phlip <phlip2005 at gmail.com> wrote:
> 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.

This sounds like an issue with the team.

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

Having done both for a few years each, in my experience I've found
that outside-in gives me better decoupled layers, where each layer is
more focused on its responsibility. Inside-out usually leaked a lot of
things into outer layers, because as I was building out I was making
assumptions on what I was going to need.
Individually, each thing that leaked was small and manageable, but a
few weeks, and months later, all of those little manageable oddities
now may the code base hell to work with, slowing down progress and
making it much harder for new devs to get ramped up.

I have worked bottom-up with Java and Ruby, and I've shipped those
apps. Not all of them were webapps either. IME, the bottom up approach
works. But after sharpening my outside-in skills, I have found it to
be all around much better development approach for the apps that I
deliver.


>
> 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'
>      end
>    end
>
>    assert_xhtml_flunk SAMPLE_LIST do
>      without! do
>        any! 'Billings report'
>      end
>    end
>  end
>
> 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(/\!$/, '')
>  end
>
> 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) == '*' }
>  end
>
> ...
>
>  def translate_tag(element)
>    if element.name == 'any!'
>      '*'
>    else
>      element.name.sub(/\!$/, '')
>    end
>  end
>
> 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...
>
> --
>  Phlip
>
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>



-- 
Zach Dennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com


More information about the rspec-users mailing list