Okay.
>> Incidentally, I'm not sure what you meant in the first sentence
>> about checking assumptions about the interface. What assumptions
>> are there to check beyond the existence of a prototype and what
>> types are involved? Or are you talking about extending the
>> behavior of an existing function, where some implementation (of
>> different functionality) has been done earlier?
>
> In one recent example I wasn't sure which would work best form the
> caller's perspective, returning results or using an in/out parameter.
I see. It isn't exactly assumptions about the interface, but
questions about how the interface should be written. That
makes sense.
>>> Once I'm done, I know the code does what I intend it to do (it
>>> passes the tests) and because they were added to pass tests, all
>>> of the code paths are tested. I know that the function is lean:
>>> there's no potentially buggy "I might need this later" code. I
>>> don't believe you can get that level of consistent test coverage
>>> but adding tests after the fact. The tests fully define the
>>> requirements for the function and unlike external requirements,
>>> they can never be out of date.
>>
>> To me these comments all come across as why TDD is good, or
>> perhaps a mixture of why TDD is good and something about how you
>> do it. It's a sales pitch - "Not gonna say what this is exactly,
>> but if you use it the results will great." I don't think this is
>> the reaction you want.
>
> When you have been doing something for a white it can be hard to be
> objective and identify things that aren't as obvious as they appear.
I should have said earlier, I didn't mean for this to be a
complaint, just to provide feedback. It might be useful
information, it just isn't what I was looking for.
The distinction I'm trying to get at has to do with what the unit
of implementation is. The wikipedia article seems to say this is
one feature (ie, per pass through the test/code loop), whereas
your description seems to say a feature may be subdivided into
smaller pieces, with each piece constituting one pass through the
test/code loop. I admit that the wikipedia article could be read
either way, this is just how it struck me. Having said all that,
let me rephrase my question. What is the unit of "work" for one
pass through the test/code loop? If that is (or can be) smaller
than "one feature", how do you choose how much to do? Your
example suggests that the amount chosen depends (or can depend)
on the implementation strategy, ie, some idea of how a function
will (or might) be implemented informs the test-writing step. Is
this a fair interpretation? If so, how common is it? Also a
related question: the example you gave seems a better fit to a
bottom-up implementation process than a top-down process - would
you say this assessment is accurate? Is your own implementation
process more top-down or bottom-up (in your estimation)?
>> Third, do the tests written /define/ the requirements or do they
>> just /verify/ some external requirements?
>
> The tests fit in where some process have a software requirements
> specification or detailed design specification. I consider them to a
> live software requirements specification.
I think a better way to say this may be that they define your
interpretation (or that of the development group) of what the
requirements say.
>> If the former, how is
>> it determined that the (test-defined) requirements match customer
>> expectations?
>
> On my projects, the developers and the testers both work form the
> product requirement specification. The unit tests are the developers
> interpretation or the requirements and the acceptance tests written be
> the testers are their interpretation.
>
>> If the latter, how is it determined that the tests
>> are faithful to the external requirements? (This question is
>> prompted in part by the statement in your last paragraph, but I
>> think the distinction is important in either case.)
>
> The code passes the acceptance tests. if it doesn't, either the
> testers or the developers misinterpreted the requirements or (more
> often in my experience), the requirements were ambiguous.
This paragraph and the two before it answer my question quite
nicely.
> The effect of the to teams writing tests for the same
> requirements is a good way of checking the quality of the
> product requirement document.
It is /a/ way. I wouldn't say it's a good way until (1) there is
some sort of metric, even if a subjective one, of what we hope
the checking process will achieve, and (2) there is at least one
(non-empty) alternative to compare it against.
>> Fourth, as tests are written before any code is written, the
>> tests are necessarily "black box" tests. This seems at odds
>> with the earlier statement about all code paths being tested.
>
> Not at all. If the only code in the function is written to pass a
> test, it is almost by definition tested.
But a single unit of implementation might contain many code
paths. How can you be sure that all paths are tested? To make
sure each code path is exercised, generally testing needs to be
white box testing rather that black box testing, ie, the tests
are written with knowledge of how the code written. Here that
is not the case. Do you see what I'm getting at here?
>> Does this mean there is some sort of restriction about what
>> kinds of code may be added at each stage?
>
> No.
So a single piece of implementation (ie, corresponding to one
test) could define as many functions as you want, with the
definition of any or all functions being as elaborate as you
want? Since the tests are written first, we can't be sure they
will exercise all the different control flows that might take
place; in general, we can't be sure that every function is even
ever called. How can these two seemingly contradictory results
be reconciled?
>> Isn't there a big
>> advantage, in some cases, to do "white box" testing rather
>> than (or in addition to) "black box" testing? Is TDD really
>> limited to just "black box" testing? If so, where is the
>> slack taken up when "white box" testing is needed? If not,
>> what is the actual process for TDD, which would permit "white
>> box" testing as well as "black box" testing?
>
> I can't see a clear distinction between "white box" and "black box"
> testing in this context.
Black box test code is written without any knowledge of the code
under test. White box test code is written with knowledge of the
code under test. White box testing therefore may examine certain
cases known to be "critical" or "near critical" for the specific
code being tested; black box testing cannot. The advantage of
black box testing is that it works no matter how a function is
implemented. The advantage of white box testing is that it may
be able to do a more thorough job of testing the code that has
actually been written.
For example, suppose we are implementing a routine to do
floating point addition in software. There may be different
code paths for corresponding to various situations - when the
exponents are the same versus when they are different, whether
or not "extraordinary rounding" may occur, whether a subnormal
result might be produced, etc. Note that these conditions are
not necessarily reflected in the output value. If test code is
written before the implementation is done, it won't know about
what these corner cases are, since they depend on how the code
is written, not any property inherent in the problem definition.
Does this make sense?