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

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@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to