Using Google Test to Unit Test C Code

While the Google Test framework is designed for C++ it can be used to create a framework for C unit testing.  Reference the Google Test documentation for more details on the capabilities of Google Test.

The Framework

These framework files are included in any C unit test project.

  • A header file to define macros to interface from C++ to C – gtest_c.h
  • A header file to provide C interfaces to the Google Test comparison functions – Gtest_helper.h.
  • A helper file to interface Google Test comparison function to C – Gtest_helper.cc

Creating Tests

Testing a C module (see example Sample.c/h below) requires two source files to define the unit tests. A C++ file listing each test – for an example see Sample_test.cc and a C file that contains the functions that execute the tests – for an example see Sample_helper.c.

In the C++ file the C_TEST macro is used to create a Google Test.  This macro will create a test in the C++ framework using the Google Test TEST macro. From within this test a C function is called to execute the C unit tests. For example:

C_TEST(Sample, Factorial)

Requires a corresponding C test function be created:

void Sample_Factorial(void)

See the C Sample_helper.c example file.  This is the function that Google Test will call to execute this test.  In this function the unit under test is called and the expected output is verified using the C interface to the Google Test comparison functions.  Also in this file are the required stubs for functions called from the unit under test.

 Google Test Output

Output from a passing set of 2 tests:
1> [==========] Running 2 tests from 1 test case.
1> [----------] Global test environment set-up.
1> [----------] 2 tests from Sample
1> [ RUN ] Sample.Factorial
1> [ OK ] Sample.Factorial (0 ms)
1> [ RUN ] Sample.Fibonacci
1> [ OK ] Sample.Fibonacci (1 ms)
1> [----------] 2 tests from Sample (1 ms total)
1>
1> [----------] Global test environment tear-down
1> [==========] 2 tests from 1 test case ran. (1 ms total)
1> [ PASSED ] 2 tests.

Simulated failure – by changing the expected value:

ExpectEqual(2, Factorial(2));
ExpectEqual(2, MultiplyInvokeCount);

Resulting failures in output from the tests:
1>  [==========] Running 2 tests from 1 test case.
1>  [----------] Global test environment set-up.
1>  [----------] 2 tests from Sample
1>  [ RUN      ] Sample.Factorial
1>..\Gtest_helper.cc(30): error : Value of: actual
1>    Actual: 1
1>  Expected: expected
1>  Which is: 2
1>  [  FAILED  ] Sample.Factorial (1 ms)
1>  [ RUN      ] Sample.Fibonacci
1>  [       OK ] Sample.Fibonacci (0 ms)
1>  [----------] 2 tests from Sample (1 ms total)
1>
1>  [----------] Global test environment tear-down
1>  [==========] 2 tests from 1 test case ran. (1 ms total)
1>  [  PASSED  ] 1 test.
1>  [  FAILED  ] 1 test, listed below:
1>  [  FAILED  ] Sample.Factorial
1>
1>   1 FAILED TEST

 Improvements

  • Possible future additions for macros to create stub functions – similar to the Google Mock framework.
  • Provide access to more of the EXPECT_ and ASSERT_ Google Test comparison functions.

Example Code

gtest_c.h


/******************************************************************************/
/*!
\file
Helper file for Google Test to interface to C.

\copyright
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

\author
Mike Weno
*/
/******************************************************************************/

#ifndef GTEST_C_H

#define GTEST_C_H

#include “gtest/gtest.h”

#define C_TEST(TestCase, Test) \
extern “C” \
{ \
void TestCase##_##Test(void); \
} \
TEST(TestCase, Test) \
{ \
TestCase##_##Test(); \
}

#endif //ndef GTEST_C_H

/******************************************************************************/
/*
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

Developed by:
DISTek(R) Integration, Inc.
6612 Chancellor Drive Suite 600
Cedar Falls, IA 50613
Tel: 319-859-3600
*/
/******************************************************************************/

Gtest_helper.h


/******************************************************************************/
/*!
\file
Helper file for Google Test to interface to C.

\copyright
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

\author
Mike Weno
*/
/******************************************************************************/

#ifndef GTEST_HELPER_H

#define GTEST_HELPER_H

extern void ExpectTrue(unsigned int expected);
extern void ExpectEqual(unsigned int expected, unsigned int actual);

#endif //ndef GTEST_HELPER_H

/******************************************************************************/
/*
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

Developed by:
DISTek(R) Integration, Inc.
6612 Chancellor Drive Suite 600
Cedar Falls, IA 50613
Tel: 319-859-3600
*/
/******************************************************************************/

Gtest_helper.cc


/******************************************************************************/
/*!
\file
Helper file for Google Test to interface to C.

\copyright
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

\author
Mike Weno
*/
/******************************************************************************/

#include “gtest/gtest.h”

extern “C”
{
#include “gtest_helper.h”
}

/******************************************************************************/
extern “C” void ExpectTrue(unsigned int expected)
{
EXPECT_TRUE(expected != 0);
}

/******************************************************************************/
extern “C” void ExpectEqual(unsigned int expected, unsigned int actual)
{
EXPECT_EQ(expected, actual);
}

/******************************************************************************/
/*
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

Developed by:
DISTek(R) Integration, Inc.
6612 Chancellor Drive Suite 600
Cedar Falls, IA 50613
Tel: 319-859-3600
*/
/******************************************************************************/

Sample.h


/******************************************************************************/
/*!
\file
Sample unit header.

\copyright
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

\author
Mike Weno
*/
/******************************************************************************/

extern int Factorial(int n);
extern int Fibonacci(int n);

/******************************************************************************/
/*
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

Developed by:
DISTek(R) Integration, Inc.
6612 Chancellor Drive Suite 600
Cedar Falls, IA 50613
Tel: 319-859-3600
*/
/******************************************************************************/

Sample.c


/******************************************************************************/
/*!
\file
Sample unit.

\copyright
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

\author
Mike Weno
*/
/******************************************************************************/
#include “SampleInclude.h”

/******************************************************************************/
/* Function to unit test */
/******************************************************************************/
int Factorial(int n)
{
int returnValue = 0;

if (n > 0)
{
returnValue = n;
while (–n != 0)
{
returnValue = multiply(n, returnValue);
}
}

return returnValue;
}

/******************************************************************************/
/* Function to unit test */
/******************************************************************************/
int Fibonacci(int n)
{
int returnValue = 0;

if (n > 1)
{
int previousValue = 1;
int nextValue;

for (int i = 1; i < n; i++)
{
nextValue = addition(previousValue, returnValue);
previousValue = returnValue;
returnValue = nextValue;
}
}

return returnValue;
}

Sample_test.cc


/******************************************************************************/
/*!
\file
Test suite for Sample.c

\copyright
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

\author
Mike Weno
*/
/******************************************************************************/

#include “gtest_c.h”

C_TEST(Sample, Factorial)
C_TEST(Sample, Fibonacci)

/******************************************************************************/
/*
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

Developed by:
DISTek(R) Integration, Inc.
6612 Chancellor Drive Suite 600
Cedar Falls, IA 50613
Tel: 319-859-3600
*/
/******************************************************************************/

Sample_helper.c


/******************************************************************************/
/*!
\file
Test suite for Sample

\copyright
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

\author
Mike Weno
*/
/******************************************************************************/

#include “SampleInclude.h”
#include “Sample.h”
#include “gtest_helper.h”

unsigned int MultiplyInvokeCount = 0;
unsigned int AdditionInvokeCount = 0;

/******************************************************************************/
/* Google Test C Helpers */
/******************************************************************************/
void Sample_Factorial(void)
{
MultiplyInvokeCount = 0;

ExpectEqual(0, Factorial(-1));
ExpectEqual(0, MultiplyInvokeCount);

ExpectEqual(0, Factorial(0));
ExpectEqual(0, MultiplyInvokeCount);

ExpectEqual(1, Factorial(1));
ExpectEqual(0, MultiplyInvokeCount);

ExpectEqual(2, Factorial(2));
ExpectEqual(1, MultiplyInvokeCount);

}

void Sample_Fibonacci(void)
{
AdditionInvokeCount = 0;

ExpectEqual(0, Fibonacci(-1));
ExpectEqual(0, AdditionInvokeCount);

ExpectEqual(0, Fibonacci(0));
ExpectEqual(0, AdditionInvokeCount);

ExpectEqual(0, Fibonacci(1));
ExpectEqual(0, AdditionInvokeCount);

ExpectEqual(1, Fibonacci(2));
ExpectEqual(1, AdditionInvokeCount);
AdditionInvokeCount = 0;

ExpectEqual(1, Fibonacci(3));
ExpectEqual(2, AdditionInvokeCount);

}

/******************************************************************************/
/* C function stubs */
/******************************************************************************/
int multiply(int x, int y)
{
MultiplyInvokeCount++;
return x * y;
}

int addition(int x, int y)
{
AdditionInvokeCount++;
return x + y;
}

/******************************************************************************/
/*
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

Developed by:
DISTek(R) Integration, Inc.
6612 Chancellor Drive Suite 600
Cedar Falls, IA 50613
Tel: 319-859-3600
*/
/******************************************************************************/

Sample_include.h


/******************************************************************************/
/*!
\file
Sample externals.

\copyright
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

\author
Mike Weno
*/
/******************************************************************************/

extern int multiply(int x, int y);
extern int addition(int x, int y);

/******************************************************************************/
/*
Copyright (C) 2014 DISTek Integration, Inc. All Rights Reserved.

Developed by:
DISTek(R) Integration, Inc.
6612 Chancellor Drive Suite 600
Cedar Falls, IA 50613
Tel: 319-859-3600
*/