Re: c++ class design: where to put debug purpose utility class?

Discussion in 'C++' started by Phlip, Jun 29, 2008.

  1. Phlip

    Phlip Guest

    1230987za wrote:

    > But I think there is a subtle difference to define "unit". I can not
    > agree with James Kanze more on unit test *when* the unit is a class,
    > but how about the unit is a function? A class' public function?
    >
    > I am adapting Test Driven Development, TDD, into my daily programming.


    Kanze is a classically-trained "unit tester". In some circles "unit" is a QA
    concept - specifically, if a test fails, you only need to inspect one unit.

    So "units" are "things which are clearly delimited and accessible to
    inspection". That should map onto C++ classes - specifically due to
    overlapping requirements. C++ classes _should_ be "things which are clearly
    delimited and accessible to inspection". Yet sometimes, by necessity, the
    "unit" is a translation unit, or a header, or a peculiar include file, or a
    global constant.

    Under TDD, if a test fails during development, you only need to inspect (or
    revert) the most recent edit. So Developer Tests and Unit Tests have
    overlapping abilities and motivations. Many TDD tests will also allow you to
    isolate faults to a small unit.

    > Please bear with me, I am doing-it-before-learning-it. I designed my
    > class interface, without implementing the API functions, I started
    > designing my test class to test each API function. It is at this point
    > my question coming up.


    That's not really TDD. You start at the test, and write each test case to
    illustrate one aspect of your target class. Only after you get the test to
    tell you what to add to the class, next, do you add it. So imagine if your
    method .activate() did not exist yet:

    test_case
    Foo aFoo
    result = aFoo.activate
    assert result == 42

    The first time you run that, if activate does not exist yet, you add it.
    Then you run it again, and "discover" that activate has no return value. The
    third time, you run it and discover the return value is wrong. Only after
    this rigorous review of the circumstances - to determine the test is failing
    for the correct reason - do you put the actual logic inside the method.

    You repeat this aggressive testing, in small cycles (and integrating between
    each tiny twitch) to grow an interface.

    > I posted my question on 2 groups, C++ and Object, interestingly
    > enough, I see folks in C++ group unanimously think "unit" is class
    > while Object folks provide me more diverse viewpoints.


    "Class" is good for "Unit". But sometimes the Unit is one isolated group of
    switches, between microscopic test pads, deep inside a big integrated
    circuit.

    --
    Phlip
    http://assert2.rubyforge.org/
     
    Phlip, Jun 29, 2008
    #1
    1. Advertising

  2. Phlip

    James Kanze Guest

    On Jun 29, 4:10 pm, "Phlip" <> wrote:
    > 1230987za wrote:
    > > But I think there is a subtle difference to define "unit". I
    > > can not agree with James Kanze more on unit test *when* the
    > > unit is a class, but how about the unit is a function? A
    > > class' public function?


    > > I am adapting Test Driven Development, TDD, into my daily
    > > programming.


    > Kanze is a classically-trained "unit tester". In some circles
    > "unit" is a QA concept - specifically, if a test fails, you
    > only need to inspect one unit.


    You mean I'm using a word in its standardly accepted meaning.
    (Unit tests have been a required part of development for many,
    many years now.)

    > So "units" are "things which are clearly delimited and
    > accessible to inspection".


    That's more or less a definition of "unit", yes. In practice,
    units are units---they're more or less the lowest level in a
    hierarchial development.

    > That should map onto C++ classes -


    Why? That's the first time I've heard that. (There are a lot
    of cases where they do map onto C++ classes.)

    > specifically due to overlapping requirements. C++ classes
    > _should_ be "things which are clearly delimited and accessible
    > to inspection". Yet sometimes, by necessity, the "unit" is a
    > translation unit, or a header, or a peculiar include file, or
    > a global constant.


    As a minimum, a "unit" is never less than a translation unit,
    since current technology doesn't allow you to break things down
    any finer. (Of course, a translation unit is often less than a
    complete class.)

    > Under TDD, if a test fails during development, you only need
    > to inspect (or revert) the most recent edit.


    I'm sorry, but that's bullshit. Regardless of the design
    philosophy, if you have halfway decent unit tests, there is a
    high probability that the error is somehow due to the most
    recent edit. And regardless of the design philosophy, there's
    always a small chance that something in the recent change has
    triggered an error which was already there before. (That's one
    of the meanings of undefined behavior---and TDD doesn't remove
    undefined behavior from the language.)

    > So Developer Tests and Unit Tests have overlapping abilities
    > and motivations. Many TDD tests will also allow you to isolate
    > faults to a small unit.


    All unit tests allow you to more or less isolate faults to a
    small unit. None are perfect, however.

    > > Please bear with me, I am doing-it-before-learning-it. I
    > > designed my class interface, without implementing the API
    > > functions, I started designing my test class to test each
    > > API function. It is at this point my question coming up.


    > That's not really TDD. You start at the test, and write each
    > test case to illustrate one aspect of your target class.


    Which, of course, isn't true, because until you have at least
    some idea as to what the class is to do, you can't write the
    tests. You start by determining what the class is to do (in
    most cases, that means some high level design). You don't start
    by just typing in code, whether it is a test or anything else.

    (I find it very hard to conceive that in this day and age,
    people are still suggesting that we code before we think. And
    proposing it as a silver bullet, no less.)

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Jun 29, 2008
    #2
    1. Advertising

  3. Phlip

    Bo Persson Guest

    James Kanze wrote:
    > On Jun 29, 4:10 pm, "Phlip" <> wrote:
    >
    >> Kanze is a classically-trained "unit tester". In some circles
    >> "unit" is a QA concept - specifically, if a test fails, you
    >> only need to inspect one unit.

    >
    >[...]
    >> That's not really TDD. You start at the test, and write each
    >> test case to illustrate one aspect of your target class.

    >
    > Which, of course, isn't true, because until you have at least
    > some idea as to what the class is to do, you can't write the
    > tests. You start by determining what the class is to do (in
    > most cases, that means some high level design). You don't start
    > by just typing in code, whether it is a test or anything else.
    >
    > (I find it very hard to conceive that in this day and age,
    > people are still suggesting that we code before we think. And
    > proposing it as a silver bullet, no less.)


    Some people do, actually. :)

    However it depends on how religious you are, and how strictly you want
    to follow the dogmas.

    If you just want to be able to say that you do TDD, you can start out
    with the single test case

    assert(false);

    This will surely fail, and you are then free to start coding your
    design. Just remember to compiler often, and make sure that the test
    still fails!

    Eventually, you will "discover" that the fault is not in your code,
    but in the test case. Then it is time to "refine" the tests. If you
    just don't call them unit tests, but use cases, you're done.

    Instant TDD(tm).



    Bo Persson
     
    Bo Persson, Jun 29, 2008
    #3
  4. Phlip

    phlip Guest

    Bo Persson wrote:

    > James Kanze wrote:


    >> Which, of course, isn't true, because until you have at least
    >> some idea as to what the class is to do, you can't write the
    >> tests.


    > Some people do, actually. :)
    >
    > However it depends on how religious you are, and how strictly you want
    > to follow the dogmas.


    And how much benefit you'd like to get from forcing the implementation design to
    emerge. Naturally, it might follow your preliminary sketches. But it might not.

    Some people are not rigorous enough to design under such tight constraints...

    --
    Phlip
     
    phlip, Jun 29, 2008
    #4
  5. Phlip

    Ian Collins Guest

    James Kanze wrote:
    > On Jun 29, 4:10 pm, "Phlip" <> wrote:
    >
    >> That should map onto C++ classes -

    >
    > Why? That's the first time I've heard that. (There are a lot
    > of cases where they do map onto C++ classes.)
    >

    Um, we agree again!

    >
    >> That's not really TDD. You start at the test, and write each
    >> test case to illustrate one aspect of your target class.

    >
    > Which, of course, isn't true, because until you have at least
    > some idea as to what the class is to do, you can't write the
    > tests. You start by determining what the class is to do (in
    > most cases, that means some high level design). You don't start
    > by just typing in code, whether it is a test or anything else.
    >

    Here's where we differ. The above assumes you have a design which
    defines your classes.

    Often with TDD, you test and to code to a more abstract requirement and
    the design (classes) follow the tests. Sometimes there's an agreed
    interface, or a base class that is being extended, but often the tests
    are testing an action for a reaction.

    I've just been looking back through some of my tests and just about all
    of my JavaScript tests are testing reaction to events and in one large
    C++ project most of the tests test systems states. Set a threshold,
    change something, look for the reaction.

    Coupling tests too closely to classes makes refactoring much harder.

    > (I find it very hard to conceive that in this day and age,
    > people are still suggesting that we code before we think. And
    > proposing it as a silver bullet, no less.)
    >

    I don't think anyone is suggesting that. What is being suggested is a
    different way of thinking.

    --
    Ian Collins.
     
    Ian Collins, Jun 30, 2008
    #5
  6. Phlip

    James Kanze Guest

    On Jun 30, 9:35 am, Ian Collins <> wrote:
    > James Kanze wrote:
    > > On Jun 29, 4:10 pm, "Phlip" <> wrote:


    > >> That should map onto C++ classes -


    > > Why? That's the first time I've heard that. (There are a
    > > lot of cases where they do map onto C++ classes.)


    > Um, we agree again!


    > >> That's not really TDD. You start at the test, and write
    > >> each test case to illustrate one aspect of your target
    > >> class.


    > > Which, of course, isn't true, because until you have at
    > > least some idea as to what the class is to do, you can't
    > > write the tests. You start by determining what the class is
    > > to do (in most cases, that means some high level design).
    > > You don't start by just typing in code, whether it is a test
    > > or anything else.


    > Here's where we differ. The above assumes you have a design
    > which defines your classes.


    The above assumes that you have at least some idea as to what
    the class (or any other bit of code) will do before you start to
    write it. I think we both also more or less agree that unless
    it is written down, you don't really have any idea. Where we
    disagree is that you seem to consider the unit tests a form of
    being "written down", whereas I consider them code, just like
    the rest---I don't know how to write a unit test until I have
    some idea what is to be tested, i.e. what the unit shoud do.
    And IMHO, I don't know that until it is written down.

    For small applications, it's not unusual that the "written down"
    part ends up as comments in the header (in Doxygen format, for
    example). For larger applications, there'll almost always be
    some sort of high level functional decomposition before I can
    even think about "units", and that has to be written down.

    > Often with TDD, you test and to code to a more abstract
    > requirement and the design (classes) follow the tests.


    Traditionally, that has been called prototyping, not testing:).
    It's almost essential for anything which interacts with a human.
    It's of almost no use if you're implementing a protocol defined
    by a standard.

    Traditionally, you're supposed to throw the prototype out, once
    you've learned from it. A rule that is often ignored (which may
    account for the poor quality of some of the software out there).
    But I suppose that you could call throwing it out simply an
    intensive refactorizing, and the prototyping "testing".
    Especially since the "throwing out" usually doesn't consist of
    actually deleting the source code from the disk, and that the
    rewrite often involves some copy/pasting:).

    > Sometimes there's an agreed interface, or a base class that is
    > being extended, but often the tests are testing an action for
    > a reaction.


    In which case, you probably have some idea as to what the action
    or reaction should be. And perhaps even some constraints on it.
    I'd write those out, in plain French (or whatever language I
    happen to be using on the project). Maybe as comments in the
    code; maybe even as comments in the unit tests. But in a human
    language, at a higher level of abstraction than C++.

    At a low level, I tend to work as if I were doing literate
    programming, even when I'm not using literate programming tools.
    The C++ code is an end product, not the means. And it's not the
    only end product, since readability (and not just correct
    implementation) is an important issue.

    [...]

    > > (I find it very hard to conceive that in this day and age,
    > > people are still suggesting that we code before we think.
    > > And proposing it as a silver bullet, no less.)


    > I don't think anyone is suggesting that. What is being
    > suggested is a different way of thinking.


    Well, I've said from the start that you can't write any code
    without first thinking. Including a unit test. But I'd like to
    insist on the fact that if you haven't written it out in your
    native language, you haven't really thought it out.

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Jun 30, 2008
    #6
  7. Phlip

    Ian Collins Guest

    James Kanze wrote:
    > On Jun 30, 9:35 am, Ian Collins <> wrote:
    >> James Kanze wrote:

    >
    >>> Which, of course, isn't true, because until you have at
    >>> least some idea as to what the class is to do, you can't
    >>> write the tests. You start by determining what the class is
    >>> to do (in most cases, that means some high level design).
    >>> You don't start by just typing in code, whether it is a test
    >>> or anything else.

    >
    >> Here's where we differ. The above assumes you have a design
    >> which defines your classes.

    >
    > The above assumes that you have at least some idea as to what
    > the class (or any other bit of code) will do before you start to
    > write it. I think we both also more or less agree that unless
    > it is written down, you don't really have any idea.


    True.

    > Where we
    > disagree is that you seem to consider the unit tests a form of
    > being "written down", whereas I consider them code, just like
    > the rest---I don't know how to write a unit test until I have
    > some idea what is to be tested, i.e. what the unit shoud do.
    > And IMHO, I don't know that until it is written down.
    >

    Not really, the written down will be some form of requirement, be that a
    clause in a traditional requirements specification, or an XP style user
    story. Given a requirement, my view of TDD is that it's another form of
    functional decomposition. You know what your end gaol is, you know the
    steps to take you there and you follow them. The smaller those steps,
    the better.

    > For small applications, it's not unusual that the "written down"
    > part ends up as comments in the header (in Doxygen format, for
    > example). For larger applications, there'll almost always be
    > some sort of high level functional decomposition before I can
    > even think about "units", and that has to be written down.
    >

    With TDD, the names of the tests replace the comments. The tests are
    the detailed design and the examples of how to use the code. The
    acceptance tests are the higher level requirements. If these are
    written correctly, either by the customer or in a form they can
    understand, the paper requirements can be discarded. I have only had
    one client bold enough to do this!

    >> Often with TDD, you test and to code to a more abstract
    >> requirement and the design (classes) follow the tests.

    >
    > Traditionally, that has been called prototyping, not testing:).
    > It's almost essential for anything which interacts with a human.
    > It's of almost no use if you're implementing a protocol defined
    > by a standard.
    >
    > Traditionally, you're supposed to throw the prototype out, once
    > you've learned from it. A rule that is often ignored (which may
    > account for the poor quality of some of the software out there).
    > But I suppose that you could call throwing it out simply an
    > intensive refactorizing, and the prototyping "testing".
    > Especially since the "throwing out" usually doesn't consist of
    > actually deleting the source code from the disk, and that the
    > rewrite often involves some copy/pasting:).
    >

    Refactoring is part of the process of improving the design. I think you
    would enjoy "Refactoring to Patterns" by Joshua Kerievsky.

    >> Sometimes there's an agreed interface, or a base class that is
    >> being extended, but often the tests are testing an action for
    >> a reaction.

    >
    > In which case, you probably have some idea as to what the action
    > or reaction should be. And perhaps even some constraints on it.
    > I'd write those out, in plain French (or whatever language I
    > happen to be using on the project). Maybe as comments in the
    > code; maybe even as comments in the unit tests. But in a human
    > language, at a higher level of abstraction than C++.
    >

    While I'd write them out as plain C++ or what ever language I happen to
    be using on the project! In a way, they are in plain English, so long
    as the tests are short and have meaningful names. That's why I always
    forward declare the tests at the top of a file, even if the language
    (such as PHP) does not require this. If the test's name can't express
    its intent, the scope of the test is too broad.

    >
    >> I don't think anyone is suggesting that. What is being
    >> suggested is a different way of thinking.

    >
    > Well, I've said from the start that you can't write any code
    > without first thinking. Including a unit test. But I'd like to
    > insist on the fact that if you haven't written it out in your
    > native language, you haven't really thought it out.
    >

    To varying degrees of 'it'!

    I like to class the design and naming of the tests as an important part
    of this thinking process. The name (meaningful) and size (small) of the
    tests is extremely important. All to often I see novices write a
    monolithic test function with scores of asserts which tell the reader
    very little. Break that up into a sequence of well named single
    condition tests and the reader can see exactly what the code is supposed
    to do. Many small, well named tests is a clear indicator that some who
    claims to be doing TDD is. He or she can then tell what they have
    broken when they make a change without having to step through the test.

    --
    Ian Collins.
     
    Ian Collins, Jun 30, 2008
    #7
  8. Phlip

    James Kanze Guest

    On Jun 30, 1:21 pm, Ian Collins <> wrote:
    > James Kanze wrote:
    > > On Jun 30, 9:35 am, Ian Collins <> wrote:
    > >> James Kanze wrote:


    [...]
    > > Where we
    > > disagree is that you seem to consider the unit tests a form of
    > > being "written down", whereas I consider them code, just like
    > > the rest---I don't know how to write a unit test until I have
    > > some idea what is to be tested, i.e. what the unit shoud do.
    > > And IMHO, I don't know that until it is written down.


    > Not really, the written down will be some form of requirement, be that a
    > clause in a traditional requirements specification, or an XP style user
    > story. Given a requirement, my view of TDD is that it's another form of
    > functional decomposition. You know what your end gaol is, you know the
    > steps to take you there and you follow them. The smaller those steps,
    > the better.


    (I presume you mean "your end goal", but it's an interesting
    Freudian slip:). Except that you're probably American, and
    would have written jail, and not gaol.)

    The smaller the steps, the better, but to get the small steps,
    you have to know the big ones. In a small application, you can
    possibly go directly from user requirements to a detailed
    specification of each unit, but as soon as the application takes
    on a certain size, you're going to end up "designing" a number
    of intermediate levels. (In many large applications, the first
    couple of levels of functional decomposition give processes, not
    what you'd normally consider a target for a unit test.)

    > > For small applications, it's not unusual that the "written down"
    > > part ends up as comments in the header (in Doxygen format, for
    > > example). For larger applications, there'll almost always be
    > > some sort of high level functional decomposition before I can
    > > even think about "units", and that has to be written down.


    > With TDD, the names of the tests replace the comments.


    I'd be interesting in seeing that. For example, in a class that
    I'm using right now, certain functions are documented with
    'precondition: itemname != NULL". For a user, that's a very
    important precondition. How do you specify that in a test, and
    make it readable? How do you organize things so that I can
    easily find the functions which interest me, and then see the
    documentation which concerns them?

    Everything I've ever seen and done suggests that you really need
    separate detailed documentation, and that it is best done before
    writing a single line of code, test or implementation. Literate
    programming, in sum. (In the best run projects I've been on,
    the header files were actually generated automatically from the
    class documentation, done in Rational Rose.)

    > The tests are the detailed design and the examples of how to
    > use the code. The acceptance tests are the higher level
    > requirements. If these are written correctly, either by the
    > customer or in a form they can understand, the paper
    > requirements can be discarded. I have only had one client
    > bold enough to do this!


    I'm very sceptical. I don't want to have to read C++ when I
    need more general information, like the pre-conditions. Even
    supposing that there are tests that reveal them, how do you
    avoid the information getting lost in all of the other detail.

    > >> Often with TDD, you test and to code to a more abstract
    > >> requirement and the design (classes) follow the tests.


    > > Traditionally, that has been called prototyping, not
    > > testing:). It's almost essential for anything which
    > > interacts with a human. It's of almost no use if you're
    > > implementing a protocol defined by a standard.


    > > Traditionally, you're supposed to throw the prototype out,
    > > once you've learned from it. A rule that is often ignored
    > > (which may account for the poor quality of some of the
    > > software out there). But I suppose that you could call
    > > throwing it out simply an intensive refactorizing, and the
    > > prototyping "testing". Especially since the "throwing out"
    > > usually doesn't consist of actually deleting the source code
    > > from the disk, and that the rewrite often involves some
    > > copy/pasting:).


    > Refactoring is part of the process of improving the design. I
    > think you would enjoy "Refactoring to Patterns" by Joshua
    > Kerievsky.


    The problem is perhaps more psychological. If someone is told
    that they are to refactor some code, they will generally try to
    use the original code, even (perhaps) to the point of "forcing"
    the new design in order to do so. I know that many recent
    authors about refactoring insist on not being afraid to rewrite.
    But the name itself suggests that you're doing something to the
    old code (other than just throwing it out). Where as I prefer
    to take the approach that you're starting from scratch, top
    down; if some of the existing code does turn out to fit into the
    new design (and it often does), so much the better, but this
    consideration isn't taken into account until after you've done
    the new design.

    > >> Sometimes there's an agreed interface, or a base class that
    > >> is being extended, but often the tests are testing an
    > >> action for a reaction.


    > > In which case, you probably have some idea as to what the
    > > action or reaction should be. And perhaps even some
    > > constraints on it. I'd write those out, in plain French (or
    > > whatever language I happen to be using on the project).
    > > Maybe as comments in the code; maybe even as comments in the
    > > unit tests. But in a human language, at a higher level of
    > > abstraction than C++.


    > While I'd write them out as plain C++ or what ever language I
    > happen to be using on the project! In a way, they are in
    > plain English, so long as the tests are short and have
    > meaningful names. That's why I always forward declare the
    > tests at the top of a file, even if the language (such as PHP)
    > does not require this. If the test's name can't express its
    > intent, the scope of the test is too broad.


    I'm still quite sceptical: I suppose that you also break the
    tests into sections, perhaps with different namespaces, in order
    to specify exactly what functions (or functionalities) are
    concerned? And what about "external" references---often, an
    idea can be made much clearer by refering to some external
    concepts. (The documentation of my FFmt class: "An ostream
    manipulator which defines a Fortran format F. Thie manipulator
    is basically the equivalent to the "%n.mF" speicification in
    printf, or Fn.m in Fortran." How could you possibly express
    that in the name of a single test function? Of course, it's not
    complete; the documentation does go on to express in detail what
    is going on---with references to a pattern described in another
    class---but for many programmers, just that one sentence is all
    that is needed.) And how do you possibly describe the
    constraints on a policy class, used to instantiate a template?

    And that's just the low level stuff. The higher up you go, the
    less appropriate C++ becomes as the description language.

    > >> I don't think anyone is suggesting that. What is being
    > >> suggested is a different way of thinking.


    > > Well, I've said from the start that you can't write any code
    > > without first thinking. Including a unit test. But I'd
    > > like to insist on the fact that if you haven't written it
    > > out in your native language, you haven't really thought it
    > > out.


    > To varying degrees of 'it'!


    > I like to class the design and naming of the tests as an
    > important part of this thinking process. The name
    > (meaningful) and size (small) of the tests is extremely
    > important.


    Certainly. At the lowest level, I can imagine your solution
    working for some types of things. I don't see too well how it
    could apply at higher levels, however, and I don't see how it
    could be used for other types of low level things. How would
    you document that the instantiation class of std::vector must be
    CopyConstructable and Assignable, for example? How would you
    document the requirements for the Allocator? For that matter,
    do you really want to document them for std::vector, given that
    they're the same for all of the containers; isn't the solution
    adopted by the standard a lot better: give the concepts a name,
    and document what that name means elsewhere (which, when it
    comes right down to it, is what I did with FFmt---except that I
    suppose the name, Fn.m in Fortran, to be an already known
    concept).

    > All to often I see novices write a monolithic test
    > function with scores of asserts which tell the reader very
    > little. Break that up into a sequence of well named single
    > condition tests and the reader can see exactly what the code
    > is supposed to do. Many small, well named tests is a clear
    > indicator that some who claims to be doing TDD is. He or she
    > can then tell what they have broken when they make a change
    > without having to step through the test.


    Guilty as charged. My current test framework certainly doesn't
    lend itself to this sort of stuff. But then, I've never been
    motivated to modify it so that it would, since I do work in the
    opposite direction: from specification and documentation to
    code. Still, I'd be interested in seeing what you use as a test
    framework---I'm currently rewriting parts of mine to accept test
    specifications in XML, so it's a good occasion to integrate new
    ideas.

    FWIW: you can see a bit of my work at my site
    (kanze.james.neuf.fr). It doesn't explain how I got to that
    state, but for most of the stuff: I wrote the documentation in
    the header file first, then the function signatures. The
    implementation of the functions and the tests were generaly done
    in parallel, and in the more complicated cases, I'd start with
    just a few of the function signatures, and expand once they
    worked. There are more tests than I've seen in a lot of
    publicly available software, but it could still be better.

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Jun 30, 2008
    #8
  9. Phlip

    Guest

    Hi,

    Here is more realistic capture of my problem, the original post is
    over-simplified, but I do learn a lot from your guys' discussion,

    class repo {
    public:
    repo();

    /**
    * Add one repo search path, if the path satisfies all the
    requirements, this path will be stored internally.
    *
    * @returns 0 if succeeds
    */
    int addSearchPath(string path);

    /**
    * Retrieve all the item from all the search paths
    */
    std::vector<item> getAllItems();

    private:
    vector<string> pathList;

    };

    Given the above class, my unit test is to cover addSearchPath(), so
    I can do either of the followings:
    1. Just use the 2 public functions since they are the real API that
    customer code will use.
    2. Unit test code accesses the private data directly to verify
    addSearchPath().

    I guess James Kanze would suggest #1, but for my case I lean to #2.
    The reason is unit test using #1 requires quite a bit setup for class
    item while this unit test just want to test addSearchPath() does cover
    all the requirements for a valid search path, so #1 seems to me too
    much academic.

    #2 serves my purpose very well and I do not want to add more API to
    return the internal private data, I will use #define private public
    trick.

    Please throw your bricks.
     
    , Jun 30, 2008
    #9
  10. Phlip

    Phlip Guest

    Ian Collins wrote:

    >> Where we
    >> disagree is that you seem to consider the unit tests a form of
    >> being "written down", whereas I consider them code, just like
    >> the rest---I don't know how to write a unit test until I have
    >> some idea what is to be tested, i.e. what the unit shoud do.
    >> And IMHO, I don't know that until it is written down.
    >>

    > Not really, the written down will be some form of requirement, be that a
    > clause in a traditional requirements specification, or an XP style user
    > story. Given a requirement, my view of TDD is that it's another form of
    > functional decomposition. You know what your end gaol is, you know the
    > steps to take you there and you follow them. The smaller those steps,
    > the better.


    You American you!! You are just not smart enough to think of all your class
    details outside of code, where they are safe from the icky details of
    implementation, best left to servants!

    You just want to leap into coding, without being rigorous enough to prove you
    can code it all in your head first, like me!

    --
    Phlip
     
    Phlip, Jun 30, 2008
    #10
  11. Phlip

    Phlip Guest

    > std::vector<item> getAllItems();
    >
    > private:
    > vector<string> pathList;


    I would typedef every template instantiation, partly because it's
    self-documenting, and because declaring the instantiation point carefully can
    avoid obscure bugs in template expansion.

    > Given the above class, my unit test is to cover addSearchPath()


    What is the effect the added search path will have on other, external objects?

    Recall this is not "unit" testing - you are allowed to use more than one object
    at a time!

    , so
    > I can do either of the followings:
    > 1. Just use the 2 public functions since they are the real API that
    > customer code will use.
    > 2. Unit test code accesses the private data directly to verify
    > addSearchPath().


    3 - detect the effect the added search path will have on behavior.

    > I guess James Kanze would suggest #1, but for my case I lean to #2.


    He is discussing unit testing, not TDD...
     
    Phlip, Jun 30, 2008
    #11
  12. Phlip

    Ian Collins Guest

    James Kanze wrote:
    > On Jun 30, 1:21 pm, Ian Collins <> wrote:
    >> James Kanze wrote:
    >>> On Jun 30, 9:35 am, Ian Collins <> wrote:
    >>>> James Kanze wrote:

    >
    > [...]
    >>> Where we
    >>> disagree is that you seem to consider the unit tests a form of
    >>> being "written down", whereas I consider them code, just like
    >>> the rest---I don't know how to write a unit test until I have
    >>> some idea what is to be tested, i.e. what the unit shoud do.
    >>> And IMHO, I don't know that until it is written down.

    >
    >> Not really, the written down will be some form of requirement, be that a
    >> clause in a traditional requirements specification, or an XP style user
    >> story. Given a requirement, my view of TDD is that it's another form of
    >> functional decomposition. You know what your end gaol is, you know the
    >> steps to take you there and you follow them. The smaller those steps,
    >> the better.

    >
    > (I presume you mean "your end goal", but it's an interesting
    > Freudian slip:).


    It was wasn't it :)

    > Except that you're probably American, and
    > would have written jail, and not gaol.)
    >

    Hey, no need for insults! I'd have thought my spelling and timezone
    would be enough of a hint.

    I'll address the rest of the post later today when I've dug up some
    unencumbered examples.

    --
    Ian Collins.
     
    Ian Collins, Jun 30, 2008
    #12
  13. Phlip

    Ian Collins Guest

    Phlip wrote:
    > Ian Collins wrote:
    >
    >>> Where we
    >>> disagree is that you seem to consider the unit tests a form of
    >>> being "written down", whereas I consider them code, just like
    >>> the rest---I don't know how to write a unit test until I have
    >>> some idea what is to be tested, i.e. what the unit shoud do.
    >>> And IMHO, I don't know that until it is written down.
    >>>

    >> Not really, the written down will be some form of requirement, be that a
    >> clause in a traditional requirements specification, or an XP style user
    >> story. Given a requirement, my view of TDD is that it's another form of
    >> functional decomposition. You know what your end gaol is, you know the
    >> steps to take you there and you follow them. The smaller those steps,
    >> the better.

    >
    > You American you!!


    Two insults in one day, what am I doing wrong?

    --
    Ian Collins.
     
    Ian Collins, Jun 30, 2008
    #13
  14. Phlip

    James Kanze Guest

    On Jun 30, 9:13 pm, Ian Collins <> wrote:
    > James Kanze wrote:
    > > On Jun 30, 1:21 pm, Ian Collins <> wrote:
    > >> James Kanze wrote:
    > >>> On Jun 30, 9:35 am, Ian Collins <> wrote:
    > >>>> James Kanze wrote:


    > > [...]
    > >>> Where we
    > >>> disagree is that you seem to consider the unit tests a form of
    > >>> being "written down", whereas I consider them code, just like
    > >>> the rest---I don't know how to write a unit test until I have
    > >>> some idea what is to be tested, i.e. what the unit shoud do.
    > >>> And IMHO, I don't know that until it is written down.


    > >> Not really, the written down will be some form of
    > >> requirement, be that a clause in a traditional requirements
    > >> specification, or an XP style user story. Given a
    > >> requirement, my view of TDD is that it's another form of
    > >> functional decomposition. You know what your end gaol is,
    > >> you know the steps to take you there and you follow them.
    > >> The smaller those steps, the better.


    > > (I presume you mean "your end goal", but it's an interesting
    > > Freudian slip:).


    > It was wasn't it :)


    > > Except that you're probably American, and
    > > would have written jail, and not gaol.)


    > Hey, no need for insults! I'd have thought my spelling and
    > timezone would be enough of a hint.


    I didn't mean it as an insult; I'm American myself (originally,
    at any rate). Living where I do, however, I see enough of both
    spellings that I don't actually notice which is being used (and
    a lot of British programmers write "program", rather than
    "programme" for a computer program). The above comment was
    meant more as a hint, since I'm not sure how many Americans
    would recognize gaol otherwise.

    > I'll address the rest of the post later today when I've dug up
    > some unencumbered examples.


    I know the problem. You write code to get the job done, not to
    bring evidence for a certain point. Which means that most of
    the real examples are buried in a lot of stuff that has nothing
    to do with the immediate point.

    I find it just as difficult to present simple examples which
    show my own style.

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Jun 30, 2008
    #14
  15. Phlip

    James Kanze Guest

    On Jun 30, 8:20 pm, wrote:

    > Here is more realistic capture of my problem, the original
    > post is over-simplified, but I do learn a lot from your guys'
    > discussion,


    > class repo {
    > public:
    > repo();


    > /**
    > * Add one repo search path, if the path satisfies all the
    > requirements, this path will be stored internally.
    > *
    > * @returns 0 if succeeds
    > */
    > int addSearchPath(string path);


    > /**
    > * Retrieve all the item from all the search paths
    > */
    > std::vector<item> getAllItems();


    > private:
    > vector<string> pathList;
    > };


    > Given the above class, my unit test is to cover
    > addSearchPath(), so I can do either of the followings:
    > 1. Just use the 2 public functions since they are the real API that
    > customer code will use.
    > 2. Unit test code accesses the private data directly to verify
    > addSearchPath().


    Or 3: both. There are many different levels of testing, and it
    may be useful for development purposes to "see" the internals.
    (Why not specify some logging output? That way, the added code
    might be useful in the final application as well.)

    > I guess James Kanze would suggest #1,


    At some point or another, you need #1. You can't release the
    code without it.

    > but for my case I lean to #2.
    > The reason is unit test using #1 requires quite a bit setup
    > for class item while this unit test just want to test
    > addSearchPath() does cover all the requirements for a valid
    > search path, so #1 seems to me too much academic.


    #2 doesn't really cover all of the requirements, if the
    requirements include actually using the added value in some way,
    so that it affects the return value of getAllItems(). And if
    it's that much work to set it up, then you need to get a better
    test harness.

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Jun 30, 2008
    #15
  16. Phlip

    James Kanze Guest

    On Jun 30, 8:51 pm, Phlip <> wrote:
    > > std::vector<item> getAllItems();


    > > private:
    > > vector<string> pathList;


    > I would typedef every template instantiation, partly because
    > it's self-documenting, and because declaring the instantiation
    > point carefully can avoid obscure bugs in template expansion.


    For a simple case like this, I don't know, but in general, yes.

    > > Given the above class, my unit test is to cover addSearchPath()


    > What is the effect the added search path will have on other,
    > external objects?


    > Recall this is not "unit" testing - you are allowed to use
    > more than one object at a time!


    The original question was about unit testing, so it is unit
    testing. Not that that prevents him from using other objects.

    > , so


    > > I can do either of the followings:
    > > 1. Just use the 2 public functions since they are the real API that
    > > customer code will use.
    > > 2. Unit test code accesses the private data directly to verify
    > > addSearchPath().


    > 3 - detect the effect the added search path will have on behavior.


    > > I guess James Kanze would suggest #1, but for my case I lean to #2.


    > He is discussing unit testing, not TDD...


    Obviously, since the original poster apparently already has a
    design, and knows what the class is supposed to do. Regardless
    of how you arrive at the design, you need unit tests. (It was
    my impression that TDD was supposed to give them to you as a
    result of the design.)

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Jun 30, 2008
    #16
  17. Phlip

    Guest

    On Jul 1, 9:19 am, James Kanze <> wrote:
    > On Jun 30, 9:13 pm, Ian Collins <> wrote:
    > > Hey, no need for insults! I'd have thought my spelling and
    > > timezone would be enough of a hint.

    >
    > I didn't mean it as an insult; I'm American myself (originally,
    > at any rate).


    No offence taken! That was a too early in the morning joke...

    > Living where I do, however, I see enough of both
    > spellings that I don't actually notice which is being used (and
    > a lot of British programmers write "program", rather than
    > "programme" for a computer program). The above comment was
    > meant more as a hint, since I'm not sure how many Americans
    > would recognize gaol otherwise.
    >

    Ah, the joys of a common language. All the more reason to express
    intent in something unambiguous like C++, or possibly French. Joking
    asside, how often do you see N programmers interpret a written
    requirement in N different ways?

    > > I'll address the rest of the post later today when I've dug up
    > > some unencumbered examples.

    >
    > I know the problem. You write code to get the job done, not to
    > bring evidence for a certain point. Which means that most of
    > the real examples are buried in a lot of stuff that has nothing
    > to do with the immediate point.
    >
    > I find it just as difficult to present simple examples which
    > show my own style.
    >

    Life would be so much eaier if we didn't have to work for others.

    Ian.
     
    , Jun 30, 2008
    #17
  18. Phlip

    phlip Guest

    Ian Collins wrote:

    > James Kanze wrote:


    >> Phlip wrote:


    >>> That should map onto C++ classes -


    >> Why? That's the first time I've heard that. (There are a lot
    >> of cases where they do map onto C++ classes.)


    > Um, we agree again!


    Unit tests (and many TDD tests) should target "units", which are code elements
    that are clearly delimited and accessible to inspection. Failure of a true Unit
    Test must implicate nothing outside its target unit.

    Classes should also be clearly delimited and accessible to inspection.

    That does not mean units _must_ be classes. For example, if I code-reviewed your
    unit test and said, "This is a bad test because the unit it addresses is not one
    class," I have brought nothing to the code review.

    Most unit tests do indeed address classes, as a happy coincidence. A pattern of
    _all_ the tests not focusing on classes should raise a warning...

    > Here's where we differ. The above assumes you have a design which
    > defines your classes.


    I am so smart that I can heroically define all my classes in my head before
    coding them. Etc...

    BTW here's a little story about my worksite. Long before I got there, someone
    wrote a script, without tests, with about 2 000 lines of >cough< Ruby. (The
    equivalent of like 6 000 lines of C++.) It since ballooned up to like 3 000.
    Several engineer-weeks of labor in total.

    Last year, I strapped a GUI onto it, and wrote some really ugly tests that
    called the whole thing. The script calls a huge batch that runs in a background
    process, and the tests simply set up the complete input, run the same batch as
    the process would run, then checks all the results in a long list of assertions.
    The tests are just aggressive enough to make the script slightly harder to add
    bugs too, and slightly less difficult to add features to. The tests are
    painfully slow, and we only run them on integration server. We add 5 000 lines
    of crappy (yet stable) tests. (Not one of them focused on a "unit"!;)

    Recently our product manager asked for just one more feature. I got permission
    for a rewrite. I commented every line of all that cruft out, and we started again.

    Today is day 2. By day 3 (via 8-hour days), I know that we 6 coders will be
    finished with the rewrite. We are using pure TDD, and promiscuous pairing. That
    means (even if we are a hothead who _knows_ what the architecture of some module
    is going to be) every 2 hours we swap pairs, going to the module in the
    application that we are /least/ familiar with. That means we leave whatever
    awesome architecture we were about to finish in the hands of someone least
    familiar with it. Including two guys who started last week with no Ruby coding
    experience.

    By tomorrow afternoon, when we are done, any of the six guys could lead a new
    pair, if necessary, to add any new feature, anywhere in the application. And
    after pure TDD, merciless refactoring, and continuous integration, we will have
    like 600 lines of Ruby in total. The new GUI will not create a thread to run the
    script, and the script will be completely cancel-able and restartable. Oh, and
    we will have 2 000 lines of super-fast tests, too. We know the code will have
    zero bugs, and will exceed the old feature list. Our operators will be able to
    start using it around Wednesday.

    (And rest assured that if our servers crash, we will rapidly lose oodles of
    money, too!;)

    Our new code will stay clean like that for as long as this project lasts, no
    matter who works on it, or what features they add.

    I am not open to any braggadocio concocted rationales why what we are doing is
    somehow vaingloriously wrong, and that all 7 guys are somehow too stupid and
    /fanfaron/ not to notice it.

    --
    Phlip
     
    phlip, Jul 1, 2008
    #18
  19. Phlip

    phlip Guest

    >> You American you!!

    > Two insults in one day, what am I doing wrong?


    Hey - /I'm/ one, so I am totally allowed!!
     
    phlip, Jul 1, 2008
    #19
  20. Phlip

    James Kanze Guest

    On Jun 30, 11:30 pm, wrote:
    > On Jul 1, 9:19 am, James Kanze <> wrote:
    > > Living where I do, however, I see enough of both
    > > spellings that I don't actually notice which is being used (and
    > > a lot of British programmers write "program", rather than
    > > "programme" for a computer program). The above comment was
    > > meant more as a hint, since I'm not sure how many Americans
    > > would recognize gaol otherwise.


    > Ah, the joys of a common language. All the more reason to
    > express intent in something unambiguous like C++, or possibly
    > French.


    French has Québecois, just like English has American (although
    obviously, the influence of Québecois in the international
    community is somewhat less than that of American). In fact, you
    can be as precise, or as imprecise, as you want in French, just
    as you can in English (or any other language I know). As you
    want, or as you can---you'd be surprised at the number of
    engineers who don't really master their native language, despite
    what Dijkstra said: "Besides a mathematical inclination, an
    exceptionally good mastery of one's native tongue is the most
    vital asset of a competent programmer."
    (http://www.cs.utexas.edu/users/EWD/ewd04xx/EWD498.PDF)

    I think his point is somewhat related to the one I'm trying to
    make: you have to have a very clear idea as to what the code is
    supposed to do before you can write anything (test,
    implementation or documentation). And IMHO, having a clear idea
    means being able to express it in your native tongue, preferably
    in writing.

    > Joking asside, how often do you see N programmers interpret a
    > written requirement in N different ways?


    And how many times to you see written requirements which can
    easily be interpreted in N different ways?

    The first step is always to get a written requirement that
    cannot be interpreted in N different ways, and to ensure that
    all of the people working on the project are interpreting it in
    the same way. I believe that this is what you are trying to do
    with your tests, but I just don't believe that it's possible.
    The expressivity (and the precision, when one has "an
    exceptionally good mastery of one's native tongue) are orders of
    magnitude higher in a human language; after all, it's been
    evolving for centuries to reach a stage of perfection that
    allows expression of all possible ideas, with as much precision
    as desired (or as much ambiguity, if that's what is desired).

    The real key, of course, is the mastery of the human language
    being used. But my experience more or less coincides with that
    of Dijkstra: those people who can't express themselves clearly
    in their native language make a mess of the C++ as well. (And
    of course, depending on the project, you might not be using your
    native language. And while generally, the better you master
    your native language, the better you can learn to master a
    foreign language, and most truly competent programmers have no
    trouble mastering another language, there are exceptions---I
    have met one or two truly competent programmers who were
    hopeless in foreign languages, despite being able to express
    themselves perfectly in their own language.)

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Jul 1, 2008
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?RGlmZmlkZW50?=

    "Page" class and utility class

    =?Utf-8?B?RGlmZmlkZW50?=, Jan 11, 2005, in forum: ASP .Net
    Replies:
    5
    Views:
    364
    =?UTF-8?B?IkFuZGVycyBOb3LDpXMgW01DQURdIg==?=
    Jan 11, 2005
  2. Gabriel Rossetti
    Replies:
    3
    Views:
    585
    Jerry Hill
    Apr 25, 2008
  3. Replies:
    8
    Views:
    427
    Greg Herlihy
    Jun 28, 2008
  4. Phlip
    Replies:
    2
    Views:
    462
    phlip
    Jul 1, 2008
  5. Garg
    Replies:
    0
    Views:
    324
Loading...

Share This Page