Re: Test-Driven Development
From: Michael Mendelsohn (keine.Werbung.1300_at_michael.mendelsohn.de)
Date: 02/29/04
- Next message: Michael Mendelsohn: "Re: Test-Driven Development"
- Previous message: Gunnar Hjalmarsson: "Re: EOL Anchor under Windows"
- In reply to: Phlip: "Re: Test-Driven Development"
- Next in thread: goose: "Re: Test-Driven Development (was Re: Career? What languages to learn?)"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Sun, 29 Feb 2004 15:31:11 +0100
Phlip schrieb:
> Michael Mendelsohn wrote:
> > I think the "roman numeral" example was badly chosen, because that is a
> > task that is clearly specified up front. It also doesn't take too long
> > to implement.
>
> The question "what order do we write the tests" matters.
Yes.
> Given a hard task, solve the simplest condition within it.
There often is no one "simplest condition".
> To write a
> language, make it print("Hello World") first. Solve for several simple
> cases, then merge the solutions to solve the general case.
That does not always work.
And it is not always the easiest way to go, as our differnt solutions
illustrate. ;)
> (To be ridiculously accurate, one could have gone I, V, II, III, IV, but the
> benefit would only be slight.)
Well, what if I wrote the routine as a simple switch:
function roman(n: Integer):string;
begin
case n of
1: roman := 'I';
2: roman := 'II';
3: roman := 'III';
4: roman := 'IV';
else
roman := '';
end;
This would work well because as you add your test cases outside, I can
add them inside the switch. :) And if you optimzed the tests outside, so
I would optimize replying inside.
Of course this is silly. There are other goals in designing your
algorithm other than making it pass tests.
Hmm, I can complete by doing
5..8: roman := 'V'+roman(n-5);
9: roman := 'IX';
10..39: roman := 'X'+roman(n-10);
40..49: roman := 'XL'+roman(n-40);
50..89: roman := 'L'+roman(n-50);
90..99: roman := 'XC'+roman(n-90);
100..399: roman := 'C'+roman(n-100);
There's a line that corresponds to each of sybol data, and a line that
corresponds to each of your cutoff data, the solution's flexible to
allow for special cases (very easy to change so that 49 is returned as
'IL').
It is easy to prove all of my rules as well.
The design is very similar to yours, which confirms a supposition I pose
below, namely, that test design affects the algorithm design.
Strangely enough, it is also similar to my other design. I feel sure it
can be arrived at from my set of tests when it expanded to include the
other tests I outline below; you'd start with 1..9, 10..99 etc.
intervals and break them up as tests are added.
> Now suppose I wrote romanToInt(), then wrote a test that iterated from 1 to
> 100, and wrote a super-stupid roman() that returned IIIIII for 6, etc. The
> assertion would be romanToInt(roman(q)) == q.
>
> Then suppose I added to that test a new assertion; no character may appear
> more than 3 times.
>
> This would be an example of adding the tests in the wrong order. The single
> new assertion, because it challenges intermediately wrong complex test
> cases, would break everything.
I cannot see how that would "break everything". The first test still
passes; the second test does not (as it should).
You could, however, go to it more simply and test for no character
appearing more than 10..9..8.. etc. times, in descending orders; the
first 2 tests pass already, the 3rd requires some coding (do the 9
replacement line here); the 4th test (for rep 7) passes when doing the 5
replacement, which also makes rep 6 through rep4 pass; finally add the
4 replacement lines to make rep3 pass.
> Putting tests in order from simple to complex allows one to aggressively
> simplify the intermediately simple (and wrong) versions of the function, to
> seek the best abstraction.
So if you think a test is too complex, devise some simpler tests that
lead up to it! :)
This leaves you with a number of test design choices, which shape your
algorithm; in effect, your test design makes your algorithm design.
I'd rather design my algorithm directly than have it designed indirectly
through the test design.
> TDD sucks. It sucks extra as an algorithm generator. If someone invented a
> general-purpose algorithm generator, they could become a trillionaire. But
> TDD is worse than BDUF at algorithm generation.
TDD is TDUF - Test Design Up Front. :)
Ironically, as I googled for BDUF, I came across
http://xp.c2.com/BigDesignUpFront.html, which states that
"Interestingly, the Hubble's big problem came about because a test was
incorrectly set up, and the telescope was built to pass that test."
> Programming lifecycles
> should not be sensitive to initial conditions. No matter how many
> butterflies flap their dumb wings, we won't get no hurricane.
Erm, I read that to mean that program design methodologies should lead
towards good solutions in a non-chaotic manner. That may be ok some of
the time, but if creativity and genius play a role in finding good
algorithms, you have to allow for the flash of insight that changes
everything.
One of the things I took from "Peopleware" was that productive
programmers need an environment that allows them to be creative thinkers
(through not disturbing them etc.).
> Programs are only simple algorithms - iteration, recursion, etc. - and
> programs should encapsulate all hard algorithms behind simple interfaces,
> such as std::sort<>.
I read the first chapter of Stephen Wolfram's "A New Kind Science", and
it illustrates that simple algorithms can produce complex behaviour (the
algorithm that produces the Mandelbrot set pictures is also very
simple). The way you set the roman numeral test up was to test for
behaviour, and this decision makes it almost impossible to arrive at the
simple algorithm that produces it.
> So because refactoring simple algorithms is so easy that refactoring
> browsers can automate it, TDD is an adequate system to rapidly search a
> design space for a best-fit, and to create robust code with a low defect
> count as a side-effect.
Design Space cannot be exhaustively searched; the decisions that go into
the test design shape your search directions; and thus I doubt that it
is more effective at finding a "best-fit" than other methods. I also
doubt that TDDed code is more robust than code that underwent unit
testing. Do you have data on this?
Michael
-- Feel the stare of my burning hamster and stop smoking!
- Next message: Michael Mendelsohn: "Re: Test-Driven Development"
- Previous message: Gunnar Hjalmarsson: "Re: EOL Anchor under Windows"
- In reply to: Phlip: "Re: Test-Driven Development"
- Next in thread: goose: "Re: Test-Driven Development (was Re: Career? What languages to learn?)"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]