Search code examples
cexitcunit

Unit testing for exit() in C


I'm using the CUnit framework for the way it displays the testing results. (I'm a programming & S.O. newbie so step by step answers really appreciated).

Is there any way I can use the same CUnit framework for when I'm testing for functions that I expect to exit()? It doesnt seem so to me, but I'm keen to ask anyway - it would display the pass/fail result along with my other CUnit tests so its ideal.

If not, I've been looking at other noob-friendly solutions (such as this SO post), but I cannot use GOTO/setjmp/longjmp. The solution also needs to be portable.

I'm using Mac & gcc command line to run this code.

EDIT One of the suggested solutions is to use C Pre-Processor (CPP) Directive /"mocking", which looks ideal? I have used the below code in my test.c file:

#define ERROR(PHRASE) {fprintf(stderr,"Fatal Error %s occurred in %s, line %d\n",PHRASE, FILE, LINE); exit(2);} 
#ifdef ERROR(PHRASE)
#define ERROR(PHRASE) {printf("In test phase");} 
#endif 
#ifndef ERROR(PHRASE #define ERROR(PHRASE) {printf("Not In test phase");} 
#endif

Here is the error message that the terminal gives me:

test.c:30:9: warning: 'ERROR' macro redefined [-Wmacro-redefined]
#define ERROR(PHRASE) {printf("In test phase");}
        ^
test.c:26:9: note: previous definition is here
#define ERROR(PHRASE) {fprintf(stderr,"Fatal Error %s occured in %s, lin...
        ^
test.c:32:14: warning: extra tokens at end of #ifndef directive
      [-Wextra-tokens]
#ifndef ERROR(PHRASE) {printf("Not In test phase");}

Removing the (PHRASE) still gives the same errors.

EDIT If helpful for anyone else, mocking using the #ifdef was the easiest way to solve this issue in the end. This website was helpful too.


Solution

  • Just so you know what to search for, what you want to do is "mock" the exit() call. The basic idea is to choose a different implementation for the exit function, generally at compile time. Frankly, C doesn't make this particularly easy, but there are some options with varying levels of portability and intrusiveness.

    This article describes something that is pretty portable, but also fairly intrusive. Basically, you use macros and/or function pointers to toggle back and forth, which means modifying your code a bit, but honestly it's not that big of a deal.

    For something potentially less intrusive but also much less portable, this article has a couple of ideas (I believe both would work on MacOS). Here you get the linker to redirect the exit() call to another function, which you provide. The good news is that it doesn't require any modifications to your code. The bad news is that it requires you to gain the cooperation of the linker, and won't work everywhere (LD_PRELOAD won't work on Windows, and AFAIK --wrap requires GNU ld or something compatible).