Mocking
Description
MOCKABLE wrapper patterns for testing external dependencies.
Philosophy
Mock external dependencies, never our own code. External means: libraries (talloc, yyjson, curl), system calls (open, read, write, stat), and vendor inline functions.
The MOCKABLE Pattern
wrapper.h provides zero-overhead mocking via weak symbols:
// wrapper.h
#ifdef NDEBUG
#define MOCKABLE static inline // Release: zero overhead
#else
#define MOCKABLE __attribute__((weak)) // Debug: overridable
#endif
MOCKABLE int posix_write_(int fd, const void *buf, size_t count);
Using Mocks in Tests
Override the weak symbol in your test file:
// test_file.c
#include "wrapper.h"
// Override - this replaces the weak symbol
int posix_write_(int fd, const void *buf, size_t count) {
return -1; // Simulate write failure
}
void test_write_error_handling(void) {
// Now any code calling posix_write_() gets our mock
res_t r = save_file("test.txt", "content");
assert(IS_ERR(r));
}
Available Wrappers
POSIX System Calls
| Wrapper |
Wraps |
posix_open_() |
open() |
posix_close_() |
close() |
posix_read_() |
read() |
posix_write_() |
write() |
posix_stat_() |
stat() |
posix_mkdir_() |
mkdir() |
posix_access_() |
access() |
posix_rename_() |
rename() |
posix_getcwd_() |
getcwd() |
posix_pipe_() |
pipe() |
posix_fcntl_() |
fcntl() |
posix_select_() |
select() |
posix_sigaction_() |
sigaction() |
posix_ioctl_() |
ioctl() |
posix_tcgetattr_() |
tcgetattr() |
posix_tcsetattr_() |
tcsetattr() |
posix_tcflush_() |
tcflush() |
POSIX stdio
| Wrapper |
Wraps |
fopen_() |
fopen() |
fclose_() |
fclose() |
fread_() |
fread() |
fwrite_() |
fwrite() |
fseek_() |
fseek() |
ftell_() |
ftell() |
posix_fdopen_() |
fdopen() |
popen_() |
popen() |
pclose_() |
pclose() |
opendir_() |
opendir() |
C Standard Library
| Wrapper |
Wraps |
snprintf_() |
snprintf() |
vsnprintf_() |
vsnprintf() |
gmtime_() |
gmtime() |
strftime_() |
strftime() |
talloc
| Wrapper |
Wraps |
talloc_zero_() |
talloc_zero_size() |
talloc_strdup_() |
talloc_strdup() |
talloc_array_() |
talloc_zero_size() |
talloc_realloc_() |
talloc_realloc_size() |
talloc_asprintf_() |
talloc_vasprintf() |
yyjson
| Wrapper |
Wraps |
yyjson_read_file_() |
yyjson_read_file() |
yyjson_mut_write_file_() |
yyjson_mut_write_file() |
yyjson_read_() |
yyjson_read() |
yyjson_doc_get_root_() |
yyjson_doc_get_root() |
yyjson_obj_get_() |
yyjson_obj_get() |
yyjson_get_sint_() |
yyjson_get_sint() |
yyjson_get_str_() |
yyjson_get_str() |
yyjson_mut_obj_add_str_() |
yyjson_mut_obj_add_str() |
yyjson_mut_obj_add_int_() |
yyjson_mut_obj_add_int() |
yyjson_mut_obj_add_uint_() |
yyjson_mut_obj_add_uint() |
yyjson_mut_obj_() |
yyjson_mut_obj() |
yyjson_mut_arr_() |
yyjson_mut_arr() |
yyjson_mut_obj_add_val_() |
yyjson_mut_obj_add_val() |
yyjson_mut_arr_add_val_() |
yyjson_mut_arr_add_val() |
yyjson_mut_obj_add_bool_() |
yyjson_mut_obj_add_bool() |
yyjson_val_mut_copy_() |
yyjson_val_mut_copy() |
libcurl (Easy API)
| Wrapper |
Wraps |
curl_easy_init_() |
curl_easy_init() |
curl_easy_cleanup_() |
curl_easy_cleanup() |
curl_easy_perform_() |
curl_easy_perform() |
curl_easy_setopt_() |
curl_easy_setopt() |
curl_easy_getinfo_() |
curl_easy_getinfo() |
curl_easy_strerror_() |
curl_easy_strerror() |
curl_slist_append_() |
curl_slist_append() |
curl_slist_free_all_() |
curl_slist_free_all() |
libcurl (Multi API)
| Wrapper |
Wraps |
curl_multi_init_() |
curl_multi_init() |
curl_multi_cleanup_() |
curl_multi_cleanup() |
curl_multi_add_handle_() |
curl_multi_add_handle() |
curl_multi_remove_handle_() |
curl_multi_remove_handle() |
curl_multi_perform_() |
curl_multi_perform() |
curl_multi_fdset_() |
curl_multi_fdset() |
curl_multi_timeout_() |
curl_multi_timeout() |
curl_multi_info_read_() |
curl_multi_info_read() |
curl_multi_strerror_() |
curl_multi_strerror() |
libpq (PostgreSQL)
| Wrapper |
Wraps |
PQgetvalue_() |
PQgetvalue() |
pq_exec_() |
PQexec() |
pq_exec_params_() |
PQexecParams() |
PQresultStatus_() |
PQresultStatus() |
pthread
| Wrapper |
Wraps |
pthread_mutex_init_() |
pthread_mutex_init() |
pthread_mutex_destroy_() |
pthread_mutex_destroy() |
pthread_mutex_lock_() |
pthread_mutex_lock() |
pthread_mutex_unlock_() |
pthread_mutex_unlock() |
pthread_create_() |
pthread_create() |
pthread_join_() |
pthread_join() |
Internal ikigai Functions
| Wrapper |
Wraps |
ik_db_init_() |
ik_db_init() |
ik_db_message_insert_() |
ik_db_message_insert() |
ik_scrollback_append_line_() |
ik_scrollback_append_line() |
ik_repl_render_frame_() |
ik_repl_render_frame() |
ik_agent_get_provider_() |
ik_agent_get_provider() |
ik_request_build_from_conversation_() |
ik_request_build_from_conversation() |
ik_http_multi_create_() |
ik_http_multi_create() |
ik_http_multi_info_read_() |
ik_http_multi_info_read() |
ik_agent_start_tool_execution_() |
ik_agent_start_tool_execution() |
ik_agent_should_continue_tool_loop_() |
ik_agent_should_continue_tool_loop() |
ik_repl_submit_tool_loop_continuation_() |
ik_repl_submit_tool_loop_continuation() |
ik_agent_add_message_() |
ik_agent_add_message() |
ik_agent_transition_to_idle_() |
ik_agent_transition_to_idle() |
Adding New Wrappers
When you need to mock a new external dependency:
- Add declaration to
wrapper.h:
MOCKABLE return_type function_name_(args);
- Add implementation to
wrapper.c:
return_type function_name_(args) {
return original_function(args);
}
Replace direct calls in production code with the wrapper
Override in tests to inject failures
Wrapping Vendor Inline Functions
Problem: Inline functions like yyjson_doc_get_root() expand at every call site, creating untestable branches.
Solution: Wrap once, use wrapper everywhere:
// sse_parser.h
yyjson_val *yyjson_doc_get_root_wrapper(yyjson_doc *doc);
// sse_parser.c
yyjson_val *yyjson_doc_get_root_wrapper(yyjson_doc *doc) {
return yyjson_doc_get_root(doc); // Inline expands here only
}
// Test both branches once
void test_wrapper_null(void) {
assert(yyjson_doc_get_root_wrapper(NULL) == NULL);
}
void test_wrapper_valid(void) {
yyjson_doc *doc = create_test_doc();
assert(yyjson_doc_get_root_wrapper(doc) != NULL);
}
What NOT to Mock
- Our own code (refactor instead)
- Pure functions (test directly)
- Simple data structures
References
src/wrapper.h - MOCKABLE declarations
src/wrapper.c - Wrapper implementations
src/openai/sse_parser.h - Inline wrapper example