Writing unit tests along with the source code (or even before the code itself – see TDD) is currently very popular among programmers writing in languages like Java or C#. For C code, however it is a bit different. There are only a few frameworks enabling the possibility to write unit tests. One of them is quite special – it allows to mock functions. And its name is CMocka. Unfortunately there are not many resources that describes the process of setting up cmocka, especially together with cmake to allow programmers add new executables, tests and mocks without unnecessary overhead. But before showing how to do it, let’s go back to basics (if you already know them, you can skip next heading).
What is mocking?
Mocking is a mechanism that allows to substitute object, we do not want to test, with empty implementation, which we can further configure to do whatever we like, like simulate errors. Usually objects are mocked, because we import them from some external library and this is not purpose of unit testing to test these external dependencies. This is how they work in object-oriented languages like the ones mentioned at the beginning. In C, it is only a bit different in that, instead of mocking the object (which does not exist in C), we mock functions. Similarly to mocking object, this allows us to control behavior of external function and i.e. test reaction of our code to errors.
To give very short impression about the possibilities, it opens let’s say we want to test function that accepts connection to a socket. How could we test such a function without touching the accept()
function? Probably we would need another program that performs connect()
. In such a simple case it requires us to write second program to cover just one case. Then, what if we want to check reaction for failed connection establishment? Manpage tells us, ECONNABORTED
is returned in that case. So, how to force our test program to break the connection before second side gets return from accept()
function? Do you see how complex it gets?
But, what if we could link our test program (the one that calls function using accept()
) to our own accept()
? Then at first we could write our fake function so it pretends that real socket had been created and return some positive integer. In second case, we could just set errno
to ECONNABORTED
and return -1
. That’s it!
This, and by the way, much more is possible with CMocka. Let’s see how to configure simplest possible project to use cmocka with cmake.
Hey cmake, do my unit tests!
For purpose of this tutorial, let’s say we have quite simple project. We just started it, so it’s perfect time to enable unit testing for TDD approach. It consists of one console (CLI) application split into two modules. First one – program
is just main() function and another function it calls. Second one – module
provide just one function that is also called by main(). For configuration, we have two extremely short CMakeLists.txt files:
CMakeLists.txtcmake_minimum_required (VERSION 3.0) project (cmocka_template) add_subdirectory(src)
src/CMakeLists.txtadd_executable(program program.c module.c)
You can view the code itself on Github. With this configuration it is now possible to make the project with cmake. To enable testing, at first we have to append following script to main CMakeLists.txt
:
27 list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules") 28 29 # cmocka 30 option(ENABLE_TESTS "Perform unit tests after build" OFF) 31 if (ENABLE_TESTS) 32 find_package(CMocka CONFIG REQUIRED) 33 include(AddCMockaTest) 34 include(AddMockedTest) 35 add_subdirectory(test) 36 enable_testing() 37 endif(ENABLE_TESTS)
In line 27, we tell cmake to look for additional cmake scripts to include in cmake/Modules
directory. This would be required by includes in lines 33 and 34.
Then, we define option to enable/disable tests. This will allow users not willing to run unit tests, build the project without having to satisfy dependency to CMocka in line 32.
In that line FindCmocka
will be called and will setup few variables. Of our interest would be ${CMOCKA_LIBRARIES}
. This points to CMocka library, we have to link with all our test programs.
Includes in lines 33 and 34, provides functions with the analogous names, first one is part of CMocka sources, the other define simple wrapper on the first one, so user do not have to type all the parameters that usually stays the same from test to test.
At last we include another build script from subdirectory and enable testing.
With help of the mentioned add_mocked_test wrapper in simplest case, where we do not use any functions external to the module, all we have to do is call add_mocked_test(module)
. However, if we do call some external functions, we have to call it like below and provide sources with these functions:
add_mocked_test(program SOURCES ${CMAKE_SOURCE_DIR}/src/module.c)
Alternatively, we can also join many sources into library and pass it like that:
add_mocked_test(module LINK_LIBRARIES mylib)
This finally becomes -lmylib
in ld.
Simplest test
As we should have complete build script, we can now write simple test:
test/test_main.c24 #include <stdarg.h> 25 #include <stddef.h> 26 #include <setjmp.h> 27 #include <cmocka.h> 28 29 #define main __real_main 30 #include "program.c" 31 #undef main 32 33 typedef struct {int a; int b; int expected;} vector_t; 34 35 const vector_t vectors[] = { 36 {0,1,0}, 37 {1,0,0}, 38 {1,1,1}, 39 {2,3,6}, 40 }; 41 42 static void test_internal(void **state) 43 { 44 int actual; 45 int i; 46 47 for (i = 0; i < sizeof(vectors)/sizeof(vector_t); i++) 48 { 49 /* get i-th inputs and expected values as vector */ 50 const vector_t *vector = &vectors[i]; 51 52 /* call function under test */ 53 actual = internal(vector->a, vector->b); 54 55 /* assert result */ 56 assert_int_equal(vector->expected, actual); 57 } 58 } 59 60 int main() 61 { 62 const struct CMUnitTest tests[] = { 63 cmocka_unit_test(test_internal), 64 }; 65 66 return cmocka_run_group_tests(tests, NULL, NULL); 67 }
At first we have to include some headers. Then in lines 29 and 31, preprocessor hack is done. This is because we already have a main()
function used to define test suite. So to not have it redeclared, we temporarily change its name to __real_main
, so this is how our program.c/main() will be called, when included in line 30. For tests with modules not containing main()
function this is superfluous.
In lines 33-40, we define sets of data to feed into our function with expected results. This is useful if we have many such vectors. For single inputs/outputs pair this is unnecessary. In line 50 one vector is extracted from that array.
Then in line 53 function under test is called and in line 56 its value is asserted with assert_int_equal
.
Finally in main()
function we have to define list of tests in this suite and call cmocka_run_group_tests
to do rest of the job for us. Done.
add_mocked_test internals
For better understanding of the process, let’s have a short look into add_mocked_test()
function. It’s source is as simple as:
cmake/Modules/AddMockedTest.cmake32 function(add_mocked_test name) 33 # parse arguments passed to the function 34 set(options ) 35 set(oneValueArgs ) 36 set(multiValueArgs SOURCES COMPILE_OPTIONS LINK_LIBRARIES LINK_OPTIONS) 37 cmake_parse_arguments(ADD_MOCKED_TEST "${options}" "${oneValueArgs}" 38 "${multiValueArgs}" ${ARGN} ) 39 40 # define test 41 add_cmocka_test(test_${name} 42 SOURCES test_${name}.c ${ADD_MOCKED_TEST_SOURCES} 43 COMPILE_OPTIONS ${DEFAULT_C_COMPILE_FLAGS} 44 ${ADD_MOCKED_TEST_COMPILE_OPTIONS} 45 LINK_LIBRARIES ${CMOCKA_LIBRARIES} 46 ${ADD_MOCKED_TEST_LINK_LIBRARIES} 47 LINK_OPTIONS ${ADD_MOCKED_TEST_LINK_OPTIONS}) 48 49 # allow using includes from src/ directory 50 target_include_directories(test_${name} PRIVATE ${CMAKE_SOURCE_DIR}/src) 51 endfunction(add_mocked_test)
At the beginning, arguments are parsed, which is outside of the scope of this article (those interested could read the official documentation). What is important in this part is that SOURCES
from the function call becomes ADD_MOCKED_TEST_SOURCES
, COMPILE_OPTIONS
are ADD_MOCKED_TEST_COMPILE_OPTIONS
and so on.
Then in line 41 add_cmocka_test
function is called. This functions does some of the job for us. For example, we do not have to worry about defining executable, linking libraries to it and making it a test called by CTest.
Then in line 42 source file list is passed to it, so it can link them into final executable. We can also pass our own compiler and linker flags, so they are routed to it in lines 43-44 and 47.
Last thing it does is to link this test executable with all required libraries and the only requirements for CMocka to work is to pass its shared library using CMOCKA_LIBRARIES
variable, which is available thanks to finding CMocka package in CMakeLists.txt
.
Beside that, we make C headers in src/
directory visible to our test program in line 50. That’s it.
Enabling mocks
Killer feature of CMocka however is its API for creating mocked functions. To use it, on CMake side all we have to do, beside what we already did is add flags for linker (to be precise -Wl,--wrap=function_name
for every function to be mocked). As can be seen in add_mocked_test
source, we could use LINK_OPTIONS
argument for that purpose. However it is timesaving to have this integrated into the function’s interface. All we have to do is add a loop for creating argument list:
cmake/Modules/AddMockedTest.cmake40 # create link flags for mocks 41 set(link_flags "") 42 foreach (mock ${ADD_MOCKED_TEST_MOCKS}) 43 set(link_flags "${link_flags} -Wl,--wrap=${mock}") 44 endforeach(mock)
Then add new argument and modify LINK_OPTIONS
of add_cmocka_test
to pass that list:
53 LINK_OPTIONS ${link_flags} ${ADD_MOCKED_TEST_LINK_OPTIONS})
Now, it should work. Then on the source side we can create new test for existing test_program test suite:
79 static void test_main(void **state) 80 { 81 int expected = 0; 82 int actual; 83 84 /* expect parameters to printf call */ 85 expect_string(__wrap_printf, format, "%d\n"); 86 expect_value(__wrap_printf, param1, 60); 87 88 /* printf should return 3 */ 89 will_return(__wrap_printf, 3); 90 91 /* call __real_main as this is main() from program.c */ 92 actual = __real_main(0, NULL); 93 94 /* assert that main return success */ 95 assert_int_equal(expected, actual); 96 }
And write mocked object:
43 int __wrap_printf (const char *format, ...) 44 { 45 int param1; 46 47 /* extract result from vargs ('printf("%d\n", result)') */ 48 va_list args; 49 va_start(args, format); 50 param1 = va_arg(args, int); 51 va_end(args); 52 53 /* ensure that parameters match expecteds in expect_*() calls */ 54 check_expected_ptr(format); 55 check_expected(param1); 56 57 /* get mocked return value from will_return() call */ 58 return mock(); 59 }
Now let’s take a look at what is happening here. At first in function test_main
, in lines 85-86 we are telling CMocka that printf
function is expected to be called with parameter format of a certain content and param1
should have value 60
. param1
here is first variadic argument for printf
function and its name is completely arbitrary. The key to working mock is to use this same name inside of mocked function.
In line 89, we say mocked printf
to return 3
(this is number of bytes written to console and will then be ignored by code in __real_main
, but for completeness it is set to proper value here).
Finally, we call the function under test and assert its result, the same way as in the example without mocks above.
On the other side is the mocked printf implementation. In languages like Java it is common to have this mock automatically generated based on the rules provided by user. In C with CMocka it is not so easy, we have to write the mock ourselves. However it is not very hard.
As we chosen variadic function for our mock and we want to check if these variadic arguments are as expected, we have to extract them at first, which is done in lines 48-51. As a result we have param1 variable (notice that it is local variable). Remember that this must match exactly what we declared in test body using one of expect_*
functions. This name is how function-parameter pair is extracted from internal dictionaries.
Then in lines 54-55 arguments are checked for having expected contents. Note that there is no difference in how we treat positional and variadic parameters in this step.
Finally in line 58, we extract value that we want to be returned from printf
(declared in line 89). This may seem superfluous in such a simple case, however if we want use this mocked printf
in more than one test, it is really useful feature. Have also in mind that linker does not give us any interface to have two or more mocks for one function. So this way we have chance to write one universal mock.
Final word
This tutorial was made during my work on template for CMocka+CMake projects. So now, as you understand how it is done, you can just go to my Github and clone the template to make it part of your project. The template is licensed under MIT license, so I don’t care about what you do with it, as long as you leave original copyright.
Thank you for this tutorial. This is a great tutorial get started with a c project and cmocka.
Hello! How i can use that with CTest on jenkins?