2

I have to test several routines/functions that have several if statements that leads to a terminate() or exit() statement that stops the test execution.

I was wondering what's the best/correct approach in testing functions like this?

For example, if I had something like the following:

void function foo(void) 
{
     if(conditionA) 
     {
        Terminate( //include infinite while loop resulting in timeout); 
    } 
    if(conditionB) 
    {
        Terminate( //includes infinite white loop resulting in timeout);
    }
}

How do I hit conditionB when I have a test case that is true for conditionA?

I think I would have to create a separate test case for conditionB to be true (and condition A to be false). However, the test still executes conditionA because I have two test cases run. Test Case #1, will enter conditionA and exit the test. Withby Test Case #2, will not run because of Test Case #1.

So, how should I structure the flow of my testing paradigm?

Currently, I have a test suite for each function that contains several test cases.

Kala J
  • 2,040
  • 4
  • 45
  • 85
  • 1
    If you're on Unix, have the test code fork. The child runs the test that exits; the parent (test harness) collects the exit status and any other information that is relevant and determines success or failure of the test. – Jonathan Leffler Aug 21 '15 at 02:56
  • I'm not on Unix. I'm running the tests on a windows machine. – Kala J Aug 21 '15 at 15:19
  • There are ways of creating a second process on Windows too, but you'll need to research those, and decide whether they're suitable. Anyway, if you need to test code that exits and yet have the test harness continue, you need to run the exiting code in some separate process. Or do something like provide a fake exit function that doesn't really exit but does convey control back to the test harness. That tends to be harder to do reliably, though. – Jonathan Leffler Aug 21 '15 at 15:23
  • @JonathanLeffler, what if I stubbed the Terminate() function above (I edited my post) and throw a divide by error exception. Should I then assert that all the test cases where that is thrown are true/pass? Is that a good way to do it? But then I would still need to continue execution e.g. conditionB above without the exception of conditionA preventing execution of conditionB. – Kala J Aug 21 '15 at 15:28
  • The exiting functions are hard; your test program needs to exit too, eventually. – Jonathan Leffler Aug 21 '15 at 15:28

3 Answers3

1

Using exit outside of main is usually a bad idea; callers are offered little opportunity to clean up any resources sanely prior to the process terminating.

Think about it... What happens when fopen fails? What about fgets, malloc, pthread_create, socket? Do any of those call exit? Don't you think it'd be sensible to design your failure modes to be as consistent as possible with the rest of the world?

Use a return value and let the caller decide whether to terminate or not... Then you can construct separate testcases for each function and... viola! Your problem has disappeared...

autistic
  • 1
  • 3
  • 35
  • 80
  • Okay, let me actually clarify that I have several terminate functions which should stop the execution of the function if called but when it comes to unit testing, I would like to stub that terminate() which I have tried to do with instead replacing it with exit(0) is that really a bad idea? How should I be stubbing terminate and not having it affect all my other test cases? – Kala J Aug 21 '15 at 02:16
  • @KalaJ It sounds like you're used to an object oriented paradigm, where you could simply overload a method and be done with it... That's the object oriented way. If you're interested in a procedural way of solving this problem, read my answer again, and feel free to ask a question if something is unclear to you. Otherwise, choose a different language, possibly an object oriented one. – autistic Aug 21 '15 at 02:23
  • oh no, I'm not trying to use OOP. I'm stubbing c code. Have you dealt with test suite unit testing framework. You can create stubs for a specific function (like mock that function) to return a value you like. I am thinking instead of mocking terminate with exit(0), should I just be returning something else instead? – Kala J Aug 21 '15 at 02:39
  • It sounds to me like you're correct in saying that you should not mock terminate with exit(0), because in addition to terminating the code under test it terminates your test harness. Instead, mock terminate by throwing an exception, which you catch in your harness allowing you to then proceed to the next test case. If your environment does not allow you to throw an exception, you need to determine what is the right way to immediately return to the harness, and that depends too much on the details of the harness for me to have a useful opinion. – Erik Johnson Aug 21 '15 at 23:07
1

You have 2 options:
1 Run the test in separate processes - a framework like check can do that easily for you.

Check is a unit testing framework for C. It features a simple interface for defining unit tests, putting little in the way of the developer. Tests are run in a separate address space, so both assertion failures and code errors that cause segmentation faults or other signals can be caught

2 Stub the offending function - either by linking your own version of terminate() function or by using a macro to redefine terminate to your own version. i.e.
#define terminate(_dummy_) my_terminate(_dummy_)

Community
  • 1
  • 1
Tomer
  • 36
  • 3
  • What should I stub terminate to? – Kala J Aug 24 '15 at 17:00
  • You should stub _terminate()_ to your own function, which will update the test harness if _terminate()_ means unwanted behavior (i.e. use test_assert) or just count how many times it occurred by incrementing a global counter. – Tomer Aug 25 '15 at 19:20
0

Simple:

void function foo(void) 
{
     if(conditionA) 
     {
        if(conditionB) 
        exit(0); 
    } 
    else if(conditionB) 
    {
        exit(0);
    }
}
Don Larynx
  • 685
  • 5
  • 15
  • I can't change the original code. I can only test it. – Kala J Aug 21 '15 at 15:00
  • Then it's not possible. – Don Larynx Aug 21 '15 at 15:03
  • But I have several test cases like this. Does this mean I can't cover them all? I would have like 50% code coverage? – Kala J Aug 21 '15 at 15:04
  • Only if you have another function that tests for `conditionB`, otherwise you can't cover them all. – Don Larynx Aug 21 '15 at 15:07
  • What I have at my disposable is to write X amount of test cases but do not change the current code. So, I can write a test case for conditionA and a test case for conditionB. But I guess I have problems running them in isolation without the results of A affecting B. – Kala J Aug 21 '15 at 15:20