Mocking with C

It’s not beautiful

Python comes bundled with fantastic mocking support in its standard library. C has no such high level mocking support.

As was famously said by those folks at ISO who maintain the C standard:

The Committee is content to let C++ be the big and ambitious language.

The C standard is revised only once per decade, and any changes tend to reflect existing features the compilers already support.

Since there are no agreed upon mocking approaches to get into the current process, don’t expect any built in help until at least the 2030s.

There are however at least three obvious ways to do what is called “Interpositioning” and third-party high level mock libraries tend to be synaptic sugar around one or all of these techniques.

So googling for the more general term interpositioning rather than mocking tends to result in more useful search results, and a common example given is how to replace malloc with your own interpretation or wrapper of malloc. This is probably all you need to know and you can take it from here.

A lot of the examples call the compiler directly, so you end up doing cargo cult programming, trawling open source project code for the correct recipe to stick in your own Makefile.am

Other uses of interpositioning

Apart from mocking, it is worth saying that there are other (more specialised) reasons why you might like to intercept a call to a function,

Apart from reverse engineering something you don’t have the source for, the main reasons are adding encryption to things, sandboxing things and profiling things.

Like all monkey patch type approaches, if you do this with other people’s libraries, you have to track their development very strongly, and if you are doing that, you might as well submit your changes to upstream or at least ask that project for the relevant calls so you feature can be added without intercepting functions.

Of course, your interest in mocking is to write unit tests for your own libraries, so you don’t have this problem.

Types of interpositions

Back to the main topic, we interposition our replacement (i.e. mock) function at three places. One approach seems to be more popular than the other two.

The first less popular approach is to intercept the function at compile time i.e. when the source of the application code is compiled, your mock function goes into the application binary instead of the original function.

The second less popular approach is to intercept the function at runtime by your tests loading the application code as a dynamic library, this is known as “the LD_PRELOAD trick”.

Anything fed to the environment variable LD_PRELOAD in your makefile is used first.

Your mock functions are given precedence (pre-loaded) so when the application code tries to define that function, it is already there.

The last and most popular approach is to get the linker (ld) to do all the heavy lifting for you.

Giving the linker the argument –wrap and the name of the symbol(s) you want to wrap, changes how those symbols are resolved.

So in the malloc example, you call your own malloc function __wrap_malloc and then it is used instead of malloc.

The original standard library malloc is still available but gets renamed into __real_malloc.

The idea being that if your __wrap_malloc first wants to do something (like log some information or something) it can still afterwards call __real_malloc

–wrap symbol Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to __wrap_symbol.

Any undefined reference to __real_symbol will be resolved to symbol.

So you just have to use the prefix __wrap_ for your wrapper function and __real_ when you want to call the real function. A simple example is:

Or just fake it

You may be able to avoid any of this by clever use of header files, i.e. make it so when the test runner is compiled, a different header file is used for those functions that you need to mock.

Putting “if we are in the test runner, use this header file” macros in the application code will horrify purists though so at least try to put as much junk as possible in the Makefile rather than the C code.

Firstly, at compile time

Compile time: When the source code is compiled Link time: When the relocatable object files are statically linked to form an executable object file Load/run time: When an executable object file is loaded into memory, dynamically linked, and then executed.

High level mock functions only work on x86 because they go under C and inject machine code.

Some other approaches involve running tests of your C code in a C++ compiler and using extra C++ features to do the job.

Premade C wraps from people associated with the samba project

https://packages.debian.org/buster/libuid-wrapper https://packages.debian.org/buster/libnss-wrapper https://packages.debian.org/buster/libsocket-wrapper https://packages.debian.org/buster/libpam-wrapper https://packages.debian.org/buster/libresolv-wrapper

LD_PRELOAD

You put all your mocks into one library.

First load MockLibrary Second Load Application code (and standard library and third party libraries etc) Third Load Test Runner (whose main is run).