A simple unit test framework

R

Ray Tayek

nw said:
Hi,

I previously asked for suggestions on teaching testing in C++.
i missed that.
... I decided that best way to proceed
would be to teach the students how they might write their own unit
test framework,

sounds very painful
and then in a lab session see if I can get them to
write their own. ...

pm4ji, but please consider the cdt with cxxtest
(http://cxxtest.sourceforge.net/) or cute in ecplispe, these will be much
less painfull. please read this parer
(http://people.cs.vt.edu/~edwards/Resolve-2006-papers/Edwards.html) if you
have not already done so.

or, if you have visual studio 2005, you can use http://nunit.org/ and write
your tests in managed c++.

writing a test needs to be trivial or people will not write them.

thanks
 
B

Bo Persson

Ian Collins wrote:
:: Pete Becker wrote:
::: Ian Collins wrote:
:::: Pete Becker wrote:
::::
:::: If you apply TDD correctly, you only write code to pass tests, so
:::: all of your code is covered.
::::
:::
::: Suppose you're writing test cases for the function log, which
::: calculates the logarithm of its argument. Internally, it will use
::: different techniques for various ranges of argument values. But the
::: specification for log, of course, doesn't tell you this, so your
::: test cases aren't likely to hit each of those ranges, and certainly
::: won't make careful probes near their boundaries. It's only by
::: looking at the code that you can write these tests.
:::
:: Pete, I think you are missing the point of TDD.
::
:: It's easy for those unfamiliar with the process to focus on the "T"
:: and ignore the "DD". TDD is a tool for delivering better code, the
:: tests drive the design, they are not driven by it. So if I were
:: tasked with writing he function log, I'd start with a simple test,
:: say log(10) and then add more tests to cover the full range of
:: inputs. These tests would specify the behavior and drive the
:: internals of the function.
::
:: Remember, if code isn't required to pass a test, it doesn't get
:: written.
::

So Pete will pass your first test with "return 1;".

How many more tests do you expect to write, before you are sure that Pete's
code is always no more than one unit off in the last decimal?

I know that he claims that it is. How can he do that?


Bo Persson
 
I

Ian Collins

Bo said:
Ian Collins wrote:
:: Pete Becker wrote:
::: Ian Collins wrote:
:::: Pete Becker wrote:
::::
:::: If you apply TDD correctly, you only write code to pass tests, so
:::: all of your code is covered.
::::
:::
::: Suppose you're writing test cases for the function log, which
::: calculates the logarithm of its argument. Internally, it will use
::: different techniques for various ranges of argument values. But the
::: specification for log, of course, doesn't tell you this, so your
::: test cases aren't likely to hit each of those ranges, and certainly
::: won't make careful probes near their boundaries. It's only by
::: looking at the code that you can write these tests.
:::
:: Pete, I think you are missing the point of TDD.
::
:: It's easy for those unfamiliar with the process to focus on the "T"
:: and ignore the "DD". TDD is a tool for delivering better code, the
:: tests drive the design, they are not driven by it. So if I were
:: tasked with writing he function log, I'd start with a simple test,
:: say log(10) and then add more tests to cover the full range of
:: inputs. These tests would specify the behavior and drive the
:: internals of the function.
::
:: Remember, if code isn't required to pass a test, it doesn't get
:: written.
::

So Pete will pass your first test with "return 1;".
Yes.

How many more tests do you expect to write, before you are sure that Pete's
code is always no more than one unit off in the last decimal?
You miss the point as well, I wouldn't be testing Pete's code, I'd be
developing my own. TDD is *NOT* retrospective testing.
 
P

Pete Becker

Ian said:
I never underestimate the ingenuity of good testers. My testers had
complete freedom to test the product how they wanted. They ended up
producing an extremely sophisticated test environment, which being fully
automated, they didn't have to run!

When I was a QA manager I'd bristle whenever any developer said they'd
"let testers" do something. That's simply wrong. Testing is not an
adjunct to development. Testing is a profession, with technical
challenges that differ from, and often exceed in complexity, those posed
by development. The skills required to do it well are vastly different,
but no less sophisticated, than those needed to write the product
itself. Most developers think they can write good tests, but in reality,
they simply don't have the right skills, and test suites written by
developers are usually naive. A development manager who gives testers
"complete freedom" is missing the point: that's not something the
manager can give or take, it's an essential part of effective testing.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
I

Ian Collins

Pete said:
When I was a QA manager I'd bristle whenever any developer said they'd
"let testers" do something. That's simply wrong. Testing is not an
adjunct to development. Testing is a profession, with technical
challenges that differ from, and often exceed in complexity, those posed
by development. The skills required to do it well are vastly different,
but no less sophisticated, than those needed to write the product
itself. Most developers think they can write good tests, but in reality,
they simply don't have the right skills, and test suites written by
developers are usually naive. A development manager who gives testers
"complete freedom" is missing the point: that's not something the
manager can give or take, it's an essential part of effective testing.
I'm not sure if you are agreeing or disagreeing with me here!
 
B

Bo Persson

Ian Collins wrote:
:: Bo Persson wrote:
::: Ian Collins wrote:
::::: Pete Becker wrote:
:::::: Ian Collins wrote:
::::::: Pete Becker wrote:
:::::::
::::::: If you apply TDD correctly, you only write code to pass tests,
::::::: so all of your code is covered.
:::::::
::::::
:::::: Suppose you're writing test cases for the function log, which
:::::: calculates the logarithm of its argument. Internally, it will use
:::::: different techniques for various ranges of argument values. But
:::::: the specification for log, of course, doesn't tell you this, so
:::::: your test cases aren't likely to hit each of those ranges, and
:::::: certainly won't make careful probes near their boundaries. It's
:::::: only by looking at the code that you can write these tests.
::::::
::::: Pete, I think you are missing the point of TDD.
:::::
::::: It's easy for those unfamiliar with the process to focus on the
::::: "T" and ignore the "DD". TDD is a tool for delivering better
::::: code, the tests drive the design, they are not driven by it. So
::::: if I were tasked with writing he function log, I'd start with a
::::: simple test, say log(10) and then add more tests to cover the
::::: full range of inputs. These tests would specify the behavior and
::::: drive the internals of the function.
:::::
::::: Remember, if code isn't required to pass a test, it doesn't get
::::: written.
:::::
:::
::: So Pete will pass your first test with "return 1;".
:::
:: Yes.
::
::: How many more tests do you expect to write, before you are sure
::: that Pete's code is always no more than one unit off in the last
::: decimal?
:::
:: You miss the point as well, I wouldn't be testing Pete's code, I'd be
:: developing my own. TDD is *NOT* retrospective testing.
::

Ok, then. If you were to develop a log() function, how many test cases would
you need for a black box testing?


Bo Persson
 
P

Pete Becker

Bo said:
Ok, then. If you were to develop a log() function, how many test cases would
you need for a black box testing?

One for each floating-point value. <g>

Don't get too hung up on the distinction between black box and white box
testing (I'm arguing against it, even though I introduced it into the
discussion). The point is that TDD is iterative; as you understand the
implementation of log() better, you recognize it's bad spots, add test
cases that hit them, and then change the implementation in response to
those tests. But that's a more sophisticated statement than "The latest
trends are to write tests first which demonstrates the requirements,
then code (classes+methods). In this case you will not have to do a
coverage, but it is a plus", which started this subthread.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
G

Gianni Mariani

Bo Persson wrote:
....
Ok, then. If you were to develop a log() function, how many test cases would
you need for a black box testing?

Ah - that's a design question.

What are your design objectives ?

I assume you want to log various information.

Design objectives are like:

a) Must be configutable for different levels (error, warning, debug etc)
b) Must not consume compute resources when the level is not being used
c) Must be able to optimize away logging levels not wanted in release code.
d) Must be as simple as possible to use.
e) Formatting of log message must be not part of the mainline code
f) Must avoid problems with missing end logs etc
g) Must handle the order of initialization issues and still collect the
logging information even though no output has been determined
h) Must handle race conditions of multiple threads writing logs
simultaneously



...............
first unit test for logging
..............

AT_DefineTest( Log, Log, "Basic Logging test" )
{

void Run()
{

// set up a preference manager
// usually set up once for all the application
// preferences.
Ptr< ATLOGTEST_PreferenceManager * > l_pm =
new ATLOGTEST_PreferenceManager;

// set up a logging manager
ATLOGTEST_LogManager l_lm;

{

// set up a place to write logging messages
ATLOGTEST_LogWriter l_lw( l_lm );

// create a loglevel preference
Preference< int >
l_loglevel( "loglevel", ~0, "/FooProgram", l_pm );

// start a logging section (compound statement)
AT_STARTLOG(
l_lm, l_loglevel, AT_LOGLEVEL_SEVERE, "Some Description"
)

at_log("Some Variables")
<< 55 << 123.456 << "Some String";

AT_ENDLOG

AT_STARTLOG(
l_lm, l_loglevel, AT_LOGLEVEL_SEVERE, "Some Description"
)

at_log("Some Variables")
<< 55 << 123.456 << "Some String";
AT_ENDLOG

}
}
};

AT_RegisterTest( Log, Log );


Now you can ask why did we use AT_STARTLOG...AT_ENDLOG macros.

The start of a logging block has to be a macro to begin with since it
must be an if statement because of requirements b amd c.

The data is stored in a temporary log session and at AT_ENDLOG it
destructs and pushes out the log all in one go - satisfying requirement h).

You need to set up a preference value for requiremnt a. Hence the
prefrence manager.

One of my prior implementations of a logging interface used an interface
like:

STARTLOG(...) { log( "blah" ) << blah; }

.... which also satisfied requirements b and c, however, a few too many
times, a n ending "}" went astray which caused all kinds of hard to find
bugs (which worked when debug logging was turned on and failed when
debug logging was turned off... very annoying. So the {...} was
replaced with START() log( "blah" ) << blah; END which never caused any
problems whatsoever. It was about 5 years ago now so I don't remember
the exact details. We got to the point of writing a little lex script
to run thorugh the code looking for possible problems.


This is a somewhat simplistic account of the issues but the point is
that the test case was written first. The rest of the code is far more
complex.


I'm not sure if there were any more tests written for the logging
interface but it was used throughout the application so it was tested
quite alot as part of other tests.
 
G

Gianni Mariani

Pete Becker wrote:
....
When I was a QA manager I'd bristle whenever any developer said they'd
"let testers" do something. That's simply wrong. Testing is not an
adjunct to development. Testing is a profession, with technical
challenges that differ from, and often exceed in complexity, those posed
by development. The skills required to do it well are vastly different,
but no less sophisticated, than those needed to write the product
itself. Most developers think they can write good tests, but in reality,
they simply don't have the right skills, and test suites written by
developers are usually naive. A development manager who gives testers
"complete freedom" is missing the point: that's not something the
manager can give or take, it's an essential part of effective testing.

The most successful teams I worked with were teams that wrote their own
tests.

I find the distinction of being a "programmer" or a "test programmer" to
be counter productive.

Testability is a primary objective. (one of my tenets for good software
development).

Without somthing being testable, it can't be maintained. Maintaining
and developing are the same thing. Hence, can't be tested means can't
be developed.

Yes, there are edge cases where the developer is unable to fully test
the code. That's ok, tests don't have to be perfect first time, as
problems are found the developer gets to learn and develop better tests.


I almost always now resort to at least one monte-carlo test.
(construct/randomly twiddle/destruct) large numbers of times in random
orders with random data.

The latest code I did that for was this:


AT_DefineTest( MonteRangeMap, RangeMap, "Basic RangeMap test" )
{

void Run()
{

TestBoth<unsigned> l_rm;

std::srand( 39000 );

int range = 60;

for ( int i = 0; i < 10000; ++i )
{

unsigned l_begin = std::rand() % range;
unsigned l_end = std::rand() % range;

bool operation = ( 2 & std::rand() ) == 0;

if ( operation )
{
l_rm.SubtractRange( l_begin, l_end );
}
else
{
l_rm.AddRange( l_begin, l_end );
}

}
}

};

AT_RegisterTest( MonteRangeMap, RangeMap );


.... it found more bugs in my code than any of the hard coded tests I
did. I also ran this test through valgrind just to make sure I was not
messing with memory.

I find the monte carlo tests to be very effective. The first time I
started employing these tests, I was met with criticism from developers
telling me that it was testing cases that would never happen in the real
world. Well, there was one failure I was getting on my monte carlo test
at the time that I pushed to get fixed and the push back was nah-never
happens. When we finally released the code, we were getting sporadic
failures that ... guess what ... were the same failures that the monte
carlo test raised... the ones where the other engineers said could not
happen.

So, surviving a monte-carlo test is required for "testability" and
"testability is a primary objective" and so any monte-carlo test
failures are now failures of the primary objective so they go to the top
of the bug list. It's amazing how solid the codebase gets once you push
this. Not to mention the rise in productivity because the engineers are
not wasting their time finding someone else's bugs.
 
P

Pete Becker

Gianni said:
Bo Persson wrote:
...

Ah - that's a design question.

What are your design objectives ?

I assume you want to log various information.

Well, no. log() in this case is the logarithm function that I used as an
example earlier in this thread.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
P

Pete Becker

Gianni said:
Pete Becker wrote:
...

The most successful teams I worked with were teams that wrote their own
tests.

I find the distinction of being a "programmer" or a "test programmer" to
be counter productive.

I do, too, because those particular terms suggest a false hierarchy. A
better distinction might be between an application programmer and a test
programmer. The fact remains that developers rarely have the skills to
write good tests or the mindset to write good tests.
Testability is a primary objective. (one of my tenets for good software
development).

Without somthing being testable, it can't be maintained. Maintaining
and developing are the same thing. Hence, can't be tested means can't
be developed.

Yes, there are edge cases where the developer is unable to fully test
the code. That's ok, tests don't have to be perfect first time, as
problems are found the developer gets to learn and develop better tests.


I almost always now resort to at least one monte-carlo test.
(construct/randomly twiddle/destruct) large numbers of times in random
orders with random data.

Yup. Typical developer-written test: I don't understand testing well
enough to do it right, so I'll do something random and hope to hit a
problem. <g>

As I've said several times, developing and testing involve two distinct
sets of skills. Developers think they're good at testing, but any
professional tester will tell you that they aren't.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
J

James Kanze

The latest trends are to write tests first which demonstrates the
requirements, then code (classes+methods).

The latest trend where? Certainly not in any company concerned
with good management, or quality software.
In this case you will not
have to do a coverage, but it is a plus. This way, the code you write
will be minimal and easier to understand and maintain.

And will not necessarily meet requirements, or even be useful.
 
J

James Kanze

I find it makes the design and code process easier and way more fun. No
more debugging sessions!

Until the code starts actually being used.

I'll admit that it's probably more fun to just write whatever
you want, rather than to have a hard specification which has to
be met, but the companies I work for want something useful.
 
J

James Kanze

If you apply TDD correctly, you only write code to pass tests, so all of
your code is covered.

Yes, but nobody but an idiot would pay you for such a thing.
Thread safety, to site but the most obvious example, isn't
testable, so you just ignore it?

My customers want to know what the code will do, and how much
development will cost, before they allocate the resources to
develope it. Which means that I have a requirements
specification which has to be met.
 
J

James Kanze

Pete, I think you are missing the point of TDD.

It's easy for those unfamiliar with the process to focus on the "T" and
ignore the "DD". TDD is a tool for delivering better code, the tests
drive the design, they are not driven by it.

Which, of course, is entirely backwards.
So if I were tasked with
writing he function log, I'd start with a simple test, say log(10) and
then add more tests to cover the full range of inputs.

That is, of course, one solution. It's theoretically possible
for log, but it will take several hundred centuries of CPU time,
which means that it's not very practical. In practice, the way
you verify that a log function is correct is with code review,
with testing of the border cases (which implies the what Pete is
calling white box testing), to back up the review.
These tests
would specify the behavior and drive the internals of the function.

In this case, I think that the behavior is specified before
hand. It is a mathematical function, after all, and we can know
the precise result for every possible input. In practice, of
course, it isn't at all possible to test it, at least not
exhaustively.
Remember, if code isn't required to pass a test, it doesn't get written.

So your log function only has to produce correct results for the
limited set of values you use to test it? I hope I never have
to use a library you wrote.
 
J

James Kanze

Ok, then. If you were to develop a log() function, how many
test cases would you need for a black box testing?

In this case, it's not really just a question of black box
versus white box. To effectively test log(), you need something
around 1,8 E19 tests. At one test per microsecond (and I doubt
that you can do them that fast), that's over 6000 centuries.

If you want to be relatively sure of the correctness of your
function, and still deliver it on schedule, you will take two
steps:

-- you will have it reviewed by people who know numeric
processing, for possible errors, and

-- you will write tests based on knowledge of the algorithm
used, to verify the limit cases for that algorithm.

Pete's point is, I think, that you cannot do the latter until
you've decided what algorithm(s) to use. In the case of a
function like log(), you can't simply test 100% of the possible
input, and you don't know what is important to test until you've
actually written the code.

Note that the two steps above work together. The people doing
the code review also review the tests, and verify that your
choice of test cases was appropriate for the code you wrote.
 
I

Ian Collins

James said:
Until the code starts actually being used.
No, The first embedded product my team developed using TDD has had about
half a dozen defects reported in 5 years and several thousand units of
field use. They were all in bits of the code with poor tests.
I'll admit that it's probably more fun to just write whatever
you want, rather than to have a hard specification which has to
be met, but the companies I work for want something useful.
What a load of bollocks. Who said anything about "write whatever you
want"? If you are going to slag something off, please understand it and
try it first.

Please fix this!
 
I

Ian Collins

James said:
In this case, it's not really just a question of black box
versus white box. To effectively test log(), you need something
around 1,8 E19 tests. At one test per microsecond (and I doubt
that you can do them that fast), that's over 6000 centuries.

If you want to be relatively sure of the correctness of your
function, and still deliver it on schedule, you will take two
steps:

-- you will have it reviewed by people who know numeric
processing, for possible errors, and

-- you will write tests based on knowledge of the algorithm
used, to verify the limit cases for that algorithm.
Better still, sit down with someone who knows numeric processing and
write the tests and the function, together.
Pete's point is, I think, that you cannot do the latter until
you've decided what algorithm(s) to use.

It can also work the other way, you are unsure of the algorithm, so you
start be writing a few simple test cases and as these become more
complex, the algorithm finds its self. I did this with polynomial fit a
while back, stating with a test for a two point fit, then three, then a
quadratic and so on. I reached the general case in about a dozen tests.
 
A

Alf P. Steinbach

* Ian Collins:
Please fix this!

James is (for good reasons) posting via Google, which ingeniously strips
off the space at the end of the signature delimiter.

The problem with Google: just like Microsoft there seems to be a
high-level management decision to gather as much feedback as possible,
countered by low-level management decisions to sabotage that so that it
looks like it's in place, but actually impossible for anyone to report
anything (you're redirected to irrelevant places, submit buttons don't
work, nothing is actually received, no relevant category is listed, no
mail address available, and so on ad nauseam).

Hence, Google Earth places Norway in Sweden, Google Groups strips off
significant spaces, and so on and so forth, and even though thousands
and tens of thousands /try/ to report this, Google's as unwise as ever
about its failings. The price of becoming a behemoth company, and what
I'm speculating is probably the reason: lying cheating weasels are
attracted like moths to a flame, and form the lower management echelons.
Oh, sorry, this' off-topic in clc++m, but it sure felt good to get
that off my chest!
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,771
Messages
2,569,587
Members
45,099
Latest member
AmbrosePri
Top