Re: Dos and don'ts in C++ unit testing?
W. J. La Cholter wrote:
"James Kanze" <james.kanze@gmail.com> wrote in
news:1171968236.031001.216110@m58g2000cwm.googlegroups.com:
Michael Fawcett wrote:
On Feb 18, 5:23 pm, Francis Glassborow <fran...@robinton.demon.co.uk>
wrote:
In article <1171706042.378302.7...@j27g2000cwj.googlegroups.com>,
James Kanze <james.ka...@gmail.com> writes>
That's one way of doing it. Although I think you often
have to group them: it's hard to test a setter without
having written the getter, and vice versa.
True, but if I write such pairs of functions I start questioning my
design (sometimes it is fine, but often it is a symptom of limited
understanding)
What about functions like std::vector's pop_back? How would one test
pop_back without first having items in the vector? And how could one
have items in the vector without first having working (tested)
functions that put items there (e.g. push_back)?
I'm not sure that's a good example. Obviously, the first
function you write (and test) is the constructor; std::vector
has constructors which fill the vector. On the other hand, it's
pretty hard to test whether the constructor worked if you don't
have some accessors; either [] or begin()/end(). And like it or
not, you're going to (probably) test the destructor some as well
with the first tests.
Once you've got these basics (constructor, destructor and
accessors), vector actually lends itself very well to Francis'
approach; write a function, then test it. If that's what you
feel most comfortable with. (I tend to not do it this way, but
it's a personal choice, not something essential.)
This kind of gets at a point I should have made earlier. The approach
of write, test, write, test or code, compile, code, compile is
contrary to good design practice. With fast modern compilers, it
feels tolerable to do fix, check, fix, check, etc., but it encourages
instant gratification of seeing it work, as opposed to confidence in
implementation and verification by inspection.
Quite. IMHO, there is some justification in leaving this up to
the individual engineer (just as e.g. you'd let him choose his
editor). As long as his final deliverable meets the specs (as
determined by code review, unit tests, etc.), he should have a
fair degree of freedom as to how he gets there. If the instant
gratification gives him some additional personal satisfaction,
and doesn't have any negative effects on the process, why not?
The key, of course, is that it doesn't have any negative effects
on the process. I rather suspect that the attitude being
forwarded by some, that you can't write code without first
having written tests for it, will have a very negative effect.
On the other hand, if A feels more at home writing all of the
code in one block, then all of the tests (that's me), where as B
prefers breaking it up into a couple of distinct blocks, writing
the tests for each block before attacking the coding of the
next, I think it acceptable to let each organize this to what he
feels best.
Using Personal
Software Process data, I could see that when I got into that mode of
development, I bogged down with constant context-switches.
Obviously, if you're writing the test for each one or two line
function. And it may just be you---and me. Perhaps others
actually work better with constant context switches. If you're
using a personal software process, of course, you'll experiment
a little with each, measure which is better for you, and use
it. Again, personally (but in this case, I suspect that it's
fairly univeral), I like to feel productive. Using a process
which allows me to know, concretely, that I'm productive
provides a great deal of personal satisfaction.
It also suggests the design wasn't completely fleshed out before
implementation was begun, if the developer tests one thing before
implementing another. If you've implemented function1 then tested
immediately, but not function2, you may have missed a good design
optimization. Consider where function1 and function2 could both be
implemented better in terms of functionImp: rather than implementing
functionImp up-front, you need to reimplement function1 in terms of
this new functionImp. And in terms of defect density, changed code is
the same as new code, whereas reused code is almost free.
Well, I'd say that you have to design first, and make these
decisions up front. And that if two functions really can share
part of the implementation, then you probably shouldn't put them
in separate blocks. But consider for example something like
std::basic_string (ignoring for the moment that an interface
like that would never occur with good design proceedures): do
you really think it would hurt the process if a programmer
preferred writing the mutator functions first, then writing the
tests for them, before attacking all of the find functions?
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient?e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S?mard, 78210 St.-Cyr-l'?cole, France, +33 (0)1 30 23 00 34
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]