views:

953

answers:

3

Hi

After reading quite some threads here at StackOverflow, I have come to the conclusion that I should adopt to some form of test driven development/unit test (or at least explore the area).

And since we are talking about c code under Linux, I decided to give check a try (I don't know if this is the right choice but if it's no good I can always try something else later on).

But since this concept of unit test and unit test frameworks is totally new to me, I started out to do some unit test on a really small test code (but I was totally lost anyway and it felt like I was missing something).

This is what I have done so far, I created the following file:

  • main.c, a main that only calls a function called my_pow and prints the result.
  • my_pow.c, contains the function my_pow.
  • my_pow.h
  • my_pow_test.c, I figured that I should place the unit code for the my_pow function here.

(So the "normal program" is the main.c, my_pow.c and my_pow.h.)

This is my_pow.c


#include "my_pow.h"
int my_pow(int a, int b)
{
    return (a*b);
}

Then I figured that in my_pow_test.c I put something like this:


#include <check.h>
#include "my_pow.h"

START_TEST (test_my_pow)
{
    /* unit test code */
}
END_TEST

//do I need some sort off main here that calls test_my_pow?

This is basically the same as in the check manual chapter 3.1, but still not....

Could someone please push me in the right direction?

Thanks Johan


Update: No reason why I tried to use check I just thought I should start somewhere, maybe CUnit is a better choice (I think I would try that as well and then make a educated choice).

Update: Thanks @philippe for indirectly pointing out that the on-line documentation is only half of the truth, the example code that clarifies what the documentation talks about was already installed with the check package. In the Ubuntu case /usr/share/doc/check/example/tests/

Update: The code example was created so that you started out by looking at his first version, then the second one etc etc. So that you could follow how he creates a very basic test case/code from nothing up to something that is useful in a traditional TTD way.

And since my code was broken and I wanted the unit test to prove this, I cheated a little and tested against the real pow function. Something like this:


START_TEST (test_my_pow1)
{
    int resultat = my_pow(3,3);
    int math     = pow(3,3);
    fail_unless ( resultat == math,
           "Error on 3^3 != %d (%d)",math, resultat);
}

However in the future I will not reproduce what is already in the stdlibs :-)


Related:

taken from searching [c] [unit-testing].

+5  A: 

I'd be more inclined to go with CUnit which is part of the X-Unit series of test frameworks.

It's scalable to large suites of tests and has been in use for many years, hence mature.

Any reason why you didn't go with CUnit?

HTH

cheers,

Rob

Rob Wells
+6  A: 

You created a first test case. Now you need to create a test suite (a group of test cases) and a runner.

I would recommend you try to compile their example first to validate your environment, although their documentation introduces new code via diff (source patch) which I do not find very convenient.


If ever you decide to try with another framework (minunit came to my mind immediately), I can point you to a "tutorial".

philippe
+1  A: 

I've been using dejagnu for years and love it.

I started using it for embedded development because it supports very well the concept that the machine on which you're running the test program may be different than the machine on which you build the test program. A consequence of this is that testing code on multiple platforms is also well supported. Not sure if that's important. The gcc testsuite uses it. I use it for desktop development as well.

The basic idea with dejagnu is that you

  • copy the test program to the "target" (which for local testing could be the ~/tmp directory)
  • start the test program
  • print stuff to the console (which acts as input to the test program)
  • parse the output from the test program and match it with what you expect
  • decide whether that output means pass or fail

Once you've got the test program and the test scripts written, you end up doing something like this:

$ runtest
                === foo Summary ===

# of expected passes            42
foo-test built Thu Jan 15 20:09:19 PST 2009
foo-test version 0.0.0.1
runtest completed at Sun Jan 18 08:29:13 2009

The way I get there for testing a library named foo is:

  • assume the source and include files for the library are in ~/src/foo
  • create a directory named ~/src/foo/testsuite
  • write a test program named foo-test.c that has a main() that
    • processes command line args
    • - prints a prompt and sits in a loop processing "commands" where I define a command to test each function in my library. This is sort of like a command shell but specific to the library. For something like my_pow I'd define the command to take 2 args.
    • write a dejagnu (which is another layer on top of Expect (http://expect.nist.gov/, which is itself a layer on top of Tcl (http://www.tcl.tk/) function called my_pow that:
      • takes two arguments
      • calculates the expected result (in Tcl)
      • sends "my_pow " to the console
      • parses the output of the my_pow command from foo-test
      • determines whether the actual result matches the expected result
      • calls the appropriate dejagnu function (pass or fail)

Sounds hard, but it's not. It takes a little while to decide how much work to do in foo-test vs. how much to do in Tcl. I end up using a fair amount of shell (e.g. bash) functionality to do things like copy files to temp directories or look at the log files that my programs generate. So you end up getting good at all this stuff.

As far as references, there's one book on Expect that I'd say is a requirement for diving into this: http://oreilly.com/catalog/9781565920903/index.html.
Between that and an online Tcl command reference http://www.tcl.tk/man/tcl8.4/TclCmd/contents.htm and FAQ (http://www.psg.com/~joem/tcl/faq.html), you're pretty much there.

Good luck.

-DB

dbyron