How to test a static function

CUnit Testing

C Problem Overview


As applying unit-test to some C code, we run into a problem that some static function can not be called at the test file, without modifying the source code. Is there any simple or reasonable way to overcome this problem?

C Solutions


Solution 1 - C

I have a test harness. In dire cases - like trying to test a static function, I use:

#include "code_under_test.c"
...test framework...

That is, I include the whole of the file containing the function under test in the test harness. It is a last resort - but it works.

Solution 2 - C

Can you give more information as to why you can't call the function?

Is it not available because it's private to a .c file? If so, you're best bet is to use conditional compilation that allows for access to the function in order to allow for other compilation units to access it. For example

SomeHeaderSomewher.h

#if UNIT_TEST
#define unit_static 
#else
#define unit_static static
#endif

Foo.h

#if UNIT_TEST
void some_method
#endif

Foo.cpp

unit_static void some_method() ...

Solution 3 - C

For unit tests, we actually have the test code within the source file itself and we conditionally compile it in when testing. This gives the unit tests full access to all functions and file-level variables (static or otherwise).

The unit tests themselves are not static - this allows us to call the unit tests from a single super-test program which unit tests all compilation units.

When we ship the code, we conditionally compile out the unit tests but this isn't actually necessary (if you want to be certain you're shipping exactly the same code you tested).

We've always found it invaluable to have the unit tests in the same place as the code you're testing since it makes it more obvious that you need to update the tests if and when the code changes.

Solution 4 - C

No - you cannot directly test a static function without modifying the source at least a little (that is the definition of static in C - that it cannot be called from a function in a different file).

You could create a separate function within the test file that just calls the static function?

For example:

//Your fn to test
static int foo(int bar)
{
  int retVal;
  //do something
  return retVal;
}

//Wrapper fn
int test_foo(int bar)
{
  return foo(bar);
}

We usually don't test our static functions directly, but rather ensure that the logic they perform is adequately tested by different tests of the calling function.

Solution 5 - C

static functions are essentially helper functions to the public (i.e. exposed) functions. So IMO, your unit tests should call the public interface with inputs that exercise all the paths in the static function.

The output (return values / side effects) of the public function should be used to test the effect of the static.

This means you need to have appropriate stubs to 'catch' these side effects. (e.g. if a function calls file IO, you need to provide stubs to override these file IO lib functions). The best way to do this by making each test suite a seperate project/executable and avoid linking to any external lib functions. You can mock even C functions, but it's not worth the effort.

Anyway, this is the approach I've used so far and it works for me. Good luck

Solution 6 - C

#define static

This is a very bad idea. If you have a variable declared local to a function, it changes the behavior of the function. Example:

static int func(int data)
{
   static int count = 0;

   count += data;
   return count;
}

You could call the function from the unit test, as func() would be exported, however the basic functionality of the code would be modified.

--kurt

Solution 7 - C

If you are under Unix environment you can include in test file additional header yourheader_static.h with declarations of your static functions and translate obj file code_under_test.o through objdump --globalize-symbols=syms_name_file to globalize local symbols. They will be visible as if they are non-static functions.

Solution 8 - C

Just to add to the accepted answer by Jonathan Leffler, and elaborate on other's mention of a wrapper function:

  1. Create a test source file, as in test_wrapper_foo.c, where foo.c is the original.
  2. In test_wrapper_foo.c:

> #include "foo.c"
> > // Prototype > int test_wrapper_foo(); > > // wrapper function > int test_wrapper_foo() { > // static function to test > return foo(); > }

Assuming that the static function foo in foo.c has prototype: int foo(void);

  1. build test_wrapper_foo.c through your makefile instead of foo.c (note that this will not break any dependencies on functions in foo.c by other external functions)

  2. In your unit test script, call test_wrapper_foo() instead of foo().

This approach leaves the original source intact, while giving you access to the function from your test framework.

Solution 9 - C

You could add a non-static function to call the static function, then call the non-static function.

static int foo ()
{
   return 3;
}

#ifdef UNIT_TEST
int test_foo ()
{
  if (foo () == 3)
    return 0;

  return 1;
}
#endif

Solution 10 - C

If you're using Ceedling and trying to use the #include "code_under_test.c" method, the test build will fail because it will automatically try to build "code_under_test.c" once when #included and also because it's the target of the test.

I've been able to get around it by a slight modification to the code_under_test.c code and a couple of other changes. Wrap the whole code_under_test.c file with this check:

#if defined(BUILD)
...
#endif // defined(BUILD)

Add this to your test harness:

#define BUILD
#include "code_under_test.c"

Add the BUILD define to your Makefile or project config file:

# Makefile example
..
CFLAGS += -DBUILD
..

Your file will now build from your environment and when included from your test harness. Ceedling will now not be able to build the file a second time (ensure your project.yml file does NOT define BUILD).

Solution 11 - C

All the above suggested answers (except a few) suggest conditional compilation which requires modification to source. As such that shouldn't be an issue, it just would add clutter (just for testing). Rather you can do something like this.

Say your function to be tested is

static int foo(int);

You make another header file called testing_headers.h which will have the contents -

static int foo(int);
int foo_wrapper(int a) {
    return foo(a);
}

Now while compiling your c file for testing you can force include this header from the compiler options.

For clang and gcc the flag is --include. For Microsoft C compiler it is /FI.

This will require absolutely 0 change to your c file and you will be able to write a non static wrapper to your function.

If you don't want a non static wrapper, you can also create a non static global function pointer initialised to foo.

You can then call the function using this global function pointer.

Solution 12 - C

There are 2 ways to do this.

  1. Include the c source file into the unit testing source file, so the static method now is in the scope of unit testing source file and callable.

  2. Do a trick:

#define static

in the head of unit testing source file.

It will convert keyword static to "nothing" or I can say, it removes static from your c source code.

In some unit testing tool, we can use config option "pre-processor" to do this trick, just put in the config:

static=

The tool willl convert all static keyword to "nothing"

But I must say, using these ways make the static method (or variable) loses the specific meaning of it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKevin YuView Question on Stackoverflow
Solution 1 - CJonathan LefflerView Answer on Stackoverflow
Solution 2 - CJaredParView Answer on Stackoverflow
Solution 3 - CpaxdiabloView Answer on Stackoverflow
Solution 4 - CBeep beepView Answer on Stackoverflow
Solution 5 - CDusharaView Answer on Stackoverflow
Solution 6 - CguestView Answer on Stackoverflow
Solution 7 - CA. MikhaylovView Answer on Stackoverflow
Solution 8 - CGuy de CarufelView Answer on Stackoverflow
Solution 9 - CPaul BeckinghamView Answer on Stackoverflow
Solution 10 - Cgreg.lundView Answer on Stackoverflow
Solution 11 - CAjay BrahmakshatriyaView Answer on Stackoverflow
Solution 12 - CTrevorView Answer on Stackoverflow