Using `cmocka` to mock functions with pointer arguments
2026-08-23
Backstory
I recently wanted to work on some units tests of a C library I have written
years ago, just for the sake of writing C again. The library is called
swayipc and is simple client library
for interacting with the Sway window manager via IPC. For testing,
cmocka is used. Previously, the unit tests had a runtime
dependency on Sway, meaning you could not test that in a different environment
(Gnome, headless, whatever). Yeah, ikr ... Well, time to change that. Time to
take a closer look at mocking with cmocka.
Setup
There are multiple source files (swayipc.c, socket.c, events.c); at the
moment I am adapting the tests for swayipc.c. First goal is to avoid
interacting with the actual Sway IPC socket, therefore we need to mock some
functions from the socket.c file.
Although there is quite a bit documentation and examples available, I couldn't find a proper example how to mock struct references in function parameters. Well, there probably is somewhere and this is yet another cheap excuse for writing a stupid blog post :)
Anyways, the official API documentation is definitely something to check out: https://api.cmocka.org/.
In the end, it's nothing really exciting, especially with version 2.0.0 and higher; but who knows, maybe it do helps somebody.
Current State
The following code snippets show the relevant parts. The function
swayipc_get_version() should be tested (trivial, I know, but it just an
example):
// swayipc.c
int swayipc_get_version(char* buf, size_t len)
{
return swayipc_get_info(GET_VERSION, buf, len);
}
int swayipc_get_info(enum message_type type, char* buf, size_t len)
{
message_s req = {.size = 0, .type = type, .data = 0};
message_s res;
if (socket_request(swayipc_fd, &req, &res) < 0) {
perror("Cannot send request");
return -errno;
}
snprintf(buf, len, "%s", res.data);
return 0;
}
It basically calls swayipc_get_info() with a command enum. The function
swayipc_get_info() creates a message_s struct req for the request and the
respective response res struct. These are then passed to socket_request()
(defined in socket.c), which sends out the request and returns the response.
As we don't care about calling the actual low-level socket_request()
function, it needs to be mocked. However, as the res pointer is used to store
the servers reply, the mock function must write data to this as well.
/// socket.c
int socket_request(int fd, const message_s* req, message_s* res)
{
if (socket_send(fd, req) < 0) return -errno;
if (socket_recv(fd, res) < 0) return -errno;
return 0;
}
Mocking
In general, mocking functions with cmocka is done by weak linking the
function to be mocked. This can be done by creating a function with the same
signature, but with the specific prefix __wrap_. Additionally, in the linker
arguments, we explicitly add it with --wrap=<function-name-to-be-mocked>.
So, to mock the function socket_request(), we
- a) create a new function
__wrap_socket_request(), and - b) add
--wrap=socket_requestto the linker args.
Next, in our test function (let's use test_swayipc_get_info), we can
parameterize the mock function: using the functions starting with will_*, we
can add data to a queue, which can later be used by the mock function using
functions starting with mock_*.
With version >= 2.0.0, we can also give mocked items a name and reference it by
that (will_set_parameter_ptr() and mock_parameter_ptr()).
Additionally, we can also use the "typed" functions
(will_set_parameter_ptr_type() and mock_parameter_ptr_type()), which not
only verify that the type of the mocked data is correct, but also cast the
returned pointer automatically.
Mhm, maybe it's easier to show with an example. For instance:
// tester function
static void test_swayipc_get_version(void** state)
{
// prepare data for mock function
char* s = "{ \"human_readable\": \"0.5.1\", \"sway_original_version\": "
"\"1.10.1\", \"variant\": \"sway\", \"major\": 0, \"minor\": 5, "
"\"patch\": 1, "
"\"loaded_config_file_name\":,\"~\\/.config\\/sway\\/config\"}";
message_s buf = {.size = strlen(s), .type = GET_VERSION, .data = s};
// add an item with key `mocked_res` and value `&buf` of type `message_s*`
// to the mock queue
will_set_parameter_ptr_type(__wrap_socket_request, mocked_res, &buf, message_s*);
// push an unamed integer to the mock queue
will_return(__wrap_socket_request, 3);
// call the function to be tested
int ret = swayipc_get_version(data, LEN);
// verify return value and the data written to the buffer
assert_int_equal(ret, 0);
assert_string_equal(data, buf.data);
}
// mock function
int __wrap_socket_request(int fd, const message_s* req, message_s* res)
{
(void)fd;
(void)message;
// retrieve the named item
// note: do not write the pointer itself, but the data it points to ... duh
*res = *mock_parameter_ptr_type(mocked_res, message_s*);
// pops the second message from the mock queue
return mock_type(int);
}
cmocka version < 2.0.0
With cmocka version < 2.0.0, the functions will_set_parameter_ptr,
will_set_parameter_ptr_type, mock_parameter_ptr(),
mock_parameter_ptr_type() are not available, meaning we don't have any named
types. Instead, we have to work with the basic functions will_return() and
mock(), which push the data into a FIFO queue. But as long we remember the
sequence, we can pop it and cast it manually. See below:
static void test_swayipc_get_version(void** state)
{
// prepare data for mock function
char* s = "{ \"human_readable\": \"0.5.1\", \"sway_original_version\": "
"\"1.10.1\", \"variant\": \"sway\", \"major\": 0, \"minor\": 5, "
"\"patch\": 1, "
"\"loaded_config_file_name\":\"~\\/.config\\/sway\\/config\"}";
message_s buf = {.size = strlen(s), .type = GET_VERSION, .data = s};
// push pointer referencing our dummy struct to the mock queue
will_return(__wrap_socket_request, &buf);
// push an integer to the mock queue
will_return(__wrap_socket_request, 3);
// verify return value and the data written to the buffer
assert_int_equal(swayipc_get_version(data, LEN), 0);
assert_string_equal(data, out.data);
}
// mock function
int __wrap_socket_request(int fd, const message_s* message,
message_s* reply)
{
(void)fd;
(void)message;
// manual cast and copy from the mock queue into the reply buffer
memcpy(reply, (message_s*)mock(), sizeof(*reply));
// pops the second element from the mock queue
return mock_type(int);
}
Maybe not as elegant as the new API, but it works as OK too. Downside is the missing name lookup and the automatic type-check & casting.
Summary
In the end, mocking functions with pointer arguments is the same as any mocking any other type, add the pointer to the queue, make sure to cast it correctly and copy the data into the out-param in the mock function:
- Queue it with:
will_set_parameter_ptr_type(<__wrap_function>, <lookup-name>, <reference>, <type>). - In the
__wrap_*function, get the mocked data by name and copy it into the out-param:<location> = *mock_parameter_ptr_type(<name>, <type>);. - Make sure to enable the weak linking with
--wrap=<function-name-to-be-mocked>in the linker args.
For versions < 2.0.0, there's no named-parameter mock queue - use
will_return() / mock() instead, remember the queue sequence and memcpy()
the popped struct into the out-param manually.