views:

353

answers:

5

Hi,

I am trying to formalize my programming practices in preparation to do it professionally (I have been doing it as a hobby for 18 years). Unfortunately they didn’t really teach us useful stuff in school, so I am trying to learn these things on my own. One aspect that I am trying to learn is unit-testing.

I have read various things about unit-testing, but as a visual learner — who has difficulty reading anything longer than a paragraph :) — I have found nothing useful. I am looking for real examples (I code mostly in VC++, not Java or C# as most of the examples tend to be). Unfortunately most of the information I have seen on unit-testing has been long, verbose prose that vaguely describes what unit-testing is rather than showing some concrete examples (and especially not in C++).

I think that I have already been doing it for quite a while though because some code I’ve written even eight years ago seems to match the descriptions of a unit-test. However I am not really sure.

When I think of a unit-test, I conjure up images of a function that needs to be verified as working as expected, along with a bunch of calls to it with various inputs, where the output is checked against the expected result. The problem is that my “unit-tests” are not automated. Worse, making an automated unit-test would seem to be a cyclical problem since to make a test function that tests another one, would itself require being able to generate the expected results from the inputs, which would then just make it a duplicate of the function to be tested. ?!?! That means that one would have to manually enter inputs and their expected outputs, which does not sound very automated to me.

Here’s a bit of code that I’ve written that I suspect to be an example of a (not automated) unit-test.

 //Normalize returns the sign of a number (as -1 or 1), or 0
 INT  Normalize (INT num) {return num ? abs(num)/num : num;}
 UINT Normalize (UINT num) {return num ? 1            : num;}
 FLOAT Normalize (FLOAT num) {return num ? abs(num)/num : num;}
 /*
  // Normalize Unit Test
  BOOL a=FALSE, b=FALSE, c=FALSE;
  INT  d=-56,  e=34,  f=0;
  a = Normalize(d)==-1;
  b = Normalize(e)==1;
  c = Normalize(f)==0;

  BOOL g=FALSE, h=FALSE, i=FALSE;
  UINT j=-56,  k=34,  l=0;
  g = Normalize(j)==1;
  h = Normalize(k)==1;
  i = Normalize(l)==0;

  BOOL m=FALSE, n=FALSE, o=FALSE;
  FLOAT p=-56.7, q=34.5,  r=0;
  m = Normalize(p)==-1.0;
  n = Normalize(q)==1.0;
  o = Normalize(r)==0.0;

  ASSERT(a & b & c & g & h & i & m & n & o);
 */

It doesn’t log results or anything fancy, but it seems like a basic unit-test. Is it? It’s a simple and somewhat contrived example, but it’s representative of what I have basically been doing: call the function to be tested with various inputs (including all edge-cases, and one or two mid-range cases), and test whether they give the expected result. Of course this means that I have to go to all the trouble of figuring out what the expected results should be (which may or may not be easy), as well as sufficient inputs to provide complete coverage of the range of inputs, and making sure that I enter them all correctly. And again, automating it seems like a cyclical problem. I’ve tried writing a unit-test for classes as well, but that seems much harder than testing a simple function; and I have not seen any examples of C++ class tests.

I’ve also read about test frameworks, but I can’t really even picture what that would be like.

Thanks for any input.

+1  A: 

Yes. That is a unit test.

Logan Capaldo
Don't be so concrete. Offer some help to the guy
Tom Leys
Sometimes you just need to hear "yes, that is correct." He's already tried to get through explanations without much success, he's got the right idea, what more is there?
Logan Capaldo
You know, you’re both right. :)
Synetech inc.
+6  A: 

It is a unit test, but it would be hard to tell

  1. Why the assert triggered
  2. How to fix it

Usually each

Normalize(p)==-1.0

Would be in a separate assert, i.e

ASSERT(Normalize(-56.7)==-1.0)

In a test framework, you would go

ASSERT_EQUALS(Normalize(-56.7), -1.0)

The idea is that the failure will point you at this line. It should be obvious why this line failed.

I suggest you use the boost unit test framework, it will run your tests and tell which ones failed. It also provides all sorts of features that are very useful as your code size grows.

Further:

You probably know this, but normalise is a toy. You might as well write

float Normalise(float input){return input > 0? 1.0f:-1.0f;}

The main reason you have a normalise function is if you are dealing with a set of floats, i.e a vector.

some3dVectorClass Normalise(const some3dVectorClass& vec)
{
   float dist = sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z)
   if (dist != 0.0f)
   {
     return vec / dist; //Assuming a good overload here for /
   }
   return some3dVectorClass();
}
Tom Leys
Hmm, I actually have a function like the one you wrote in one of my libraries, but for some reason I also have the more complex (and *slower*) one. I wonder why I wrote that second one… :)Hehe, I just checked and I do have the vector version in the OpenGL.cpp library I wrote in University. :)
Synetech inc.
+1  A: 

This looks like a good start, although I think you could go further in testing edge cases: you should be testing things like 1 and -1 for INT and 1 and 0xFFFFFFFF for UINT (assuming UINT is 32-bits).

If you want to learn about unit testing frameworks, my best advice is to just get one and use it. Cppunit has good documentation. You don't necessarily need to understand how it works to get good use out of it or to understand better what a unit test looks like, but reading the code might be more engrossing than reading the documentation if you're a hands-on sort of person.

Nick Meyer
Thanks, actually I have looked at Cppunit, CxxTest, Boost, and several others but have not easily gotten into any of them yet.As a visual learner, I find it easiest for me to learn things by watching videos. I guess I could see if there are any demo videos for these frameworks.
Synetech inc.
I just checked YouTube for any Cppunit videos, but the results had a bunch of Japornime. lol
Synetech inc.
A: 

How do you know which of the expressions failed explicitly? Unit tests are about isolating distinct aspects of your components. a, b, c, g, h, i, m, n, o should each have a test of their own. Look at CxxTest. Its user guide also explains some concepts of unit tests and it is a really good unit testing framework for C++.

haffax
I just checked a few of my files, and while most of them have the single assert, I do recall the pain of having to step through the code when it failed. :)I guess that’s why one of my unit-tests has this at the end: for (UINT i=0; i<8; i++) {ASSERT(test[i]==testresults[i]);}
Synetech inc.
+1  A: 

Great news. I did look for some videos of unit-testing. While I haven’t found the perfect kind of demo videos, I did find some that seem to be quite useful.

This one at (go figure softwaretestingvideos.com) one has actual code samples.

And not surprisingly I suppose, Google has some good ones like this one, or this lofty one.

Synetech inc.
+1 Videos are a good idea if you like visuals.
Tom Leys