c-testing
C testing patterns: Unity framework (TEST_ASSERT_*), CMocka mocking with __wrap_ and linker flag, Valgrind leak checking, AddressSanitizer, CMake CTest integration, test organization, and running tests with sanitizers. Use when writing or reviewing C tests.
Best use case
c-testing is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
C testing patterns: Unity framework (TEST_ASSERT_*), CMocka mocking with __wrap_ and linker flag, Valgrind leak checking, AddressSanitizer, CMake CTest integration, test organization, and running tests with sanitizers. Use when writing or reviewing C tests.
Teams using c-testing should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/c-testing/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How c-testing Compares
| Feature / Agent | c-testing | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
C testing patterns: Unity framework (TEST_ASSERT_*), CMocka mocking with __wrap_ and linker flag, Valgrind leak checking, AddressSanitizer, CMake CTest integration, test organization, and running tests with sanitizers. Use when writing or reviewing C tests.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# C Testing
## When to Activate
- Writing C unit tests with Unity or CMocka
- Setting up CMake CTest integration
- Running tests with AddressSanitizer or Valgrind
- Mocking C functions for isolation testing
- Adding test coverage to a C library that currently has no automated tests and needs to be validated before a release
- Replacing a slow Valgrind CI step with a fast AddressSanitizer build that catches the same heap and stack errors
- Isolating a C module that calls a network or filesystem function by wrapping it with CMocka `__wrap_` mocks
- Wiring CTest into a CMake build so `cmake --build build && ctest` gives a single pass/fail result in CI
---
## Unity — Minimal Unit Testing
Unity is a single-file C test framework — no dependencies.
```c
/* tests/test_queue.c */
#include "unity.h"
#include "queue.h"
void setUp(void) { /* called before each test */ }
void tearDown(void) { /* called after each test */ }
void test_queue_push_and_pop(void) {
Queue *q = queue_new(4);
TEST_ASSERT_NOT_NULL(q);
int val = 42;
TEST_ASSERT_EQUAL(ERR_OK, queue_push(q, &val, sizeof(val)));
TEST_ASSERT_EQUAL(1, (int)queue_size(q));
int out = 0;
TEST_ASSERT_EQUAL(ERR_OK, queue_pop(q, &out, sizeof(out)));
TEST_ASSERT_EQUAL_INT(42, out);
TEST_ASSERT_EQUAL(0, (int)queue_size(q));
queue_free(q);
}
void test_queue_overflow(void) {
Queue *q = queue_new(1);
int a = 1, b = 2;
TEST_ASSERT_EQUAL(ERR_OK, queue_push(q, &a, sizeof(a)));
TEST_ASSERT_EQUAL(ERR_OVERFLOW, queue_push(q, &b, sizeof(b)));
queue_free(q);
}
void test_queue_null_arg(void) {
TEST_ASSERT_EQUAL(ERR_INVALID, queue_push(NULL, NULL, 0));
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_queue_push_and_pop);
RUN_TEST(test_queue_overflow);
RUN_TEST(test_queue_null_arg);
return UNITY_END();
}
```
### Unity Assertion Reference
```c
TEST_ASSERT_EQUAL(expected, actual) /* generic equality */
TEST_ASSERT_EQUAL_INT(expected, actual)
TEST_ASSERT_EQUAL_UINT(expected, actual)
TEST_ASSERT_EQUAL_STRING(expected, actual)
TEST_ASSERT_EQUAL_FLOAT(expected, actual, delta)
TEST_ASSERT_EQUAL_MEMORY(expected, actual, len)
TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, n)
TEST_ASSERT_NULL(ptr)
TEST_ASSERT_NOT_NULL(ptr)
TEST_ASSERT_TRUE(condition)
TEST_ASSERT_FALSE(condition)
TEST_FAIL_MESSAGE("explicit failure reason")
TEST_IGNORE_MESSAGE("skipped — known flake")
```
---
## CMocka — Mocking with `__wrap_`
CMocka enables function mocking via the `--wrap` linker flag.
```c
/* tests/test_http_client.c */
#include <stdarg.h>
#include <setjmp.h>
#include <cmocka.h>
#include "http_client.h"
/* Wrap the real http_get — linker replaces calls with __wrap_http_get */
int __wrap_http_get(const char *url, char *out, size_t max) {
check_expected(url);
const char *resp = mock_ptr_type(const char *);
strncpy(out, resp, max - 1);
out[max - 1] = '\0';
return mock_type(int);
}
static void test_fetch_user_success(void **state) {
(void)state;
expect_string(__wrap_http_get, url, "https://api.example.com/users/1");
will_return(__wrap_http_get, "{\"name\":\"Alice\",\"age\":30}");
will_return(__wrap_http_get, 200);
User user;
int rc = fetch_user(1, &user);
assert_int_equal(0, rc);
assert_string_equal("Alice", user.name);
assert_int_equal(30, user.age);
}
static void test_fetch_user_network_error(void **state) {
(void)state;
expect_any(__wrap_http_get, url);
will_return(__wrap_http_get, "");
will_return(__wrap_http_get, -1);
User user;
int rc = fetch_user(1, &user);
assert_int_equal(ERR_IO, rc);
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_fetch_user_success),
cmocka_unit_test(test_fetch_user_network_error),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
```
### CMake with `--wrap` linker flag
```cmake
add_executable(test_http_client
tests/test_http_client.c
src/http_client.c
)
target_include_directories(test_http_client PRIVATE src)
target_link_libraries(test_http_client cmocka)
target_link_options(test_http_client PRIVATE
-Wl,--wrap,http_get # Linux (ld)
)
# macOS: use -Wl,-interposable,_http_get or compile with -DUNIT_TEST and #ifdef
```
---
## AddressSanitizer — Fast Compile-Time Detection
Catches: heap-use-after-free, stack-buffer-overflow, use-after-return, use-after-scope, undefined behavior.
```makefile
CFLAGS_TEST = -fsanitize=address,undefined -g -O1 -fno-omit-frame-pointer
test: build/test_queue
ASAN_OPTIONS=detect_leaks=1 ./build/test_queue
build/test_queue: tests/test_queue.c src/queue.c unity/unity.c
$(CC) $(CFLAGS_TEST) -Isrc -Iunity $^ -o $@
```
ASan outputs colorized stack traces with file:line references. Exit code is non-zero on any error.
---
## Valgrind — Deep Runtime Analysis
Catches: memory leaks, invalid reads/writes, uninitialized memory, overlapping memcpy.
```bash
valgrind \
--leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--error-exitcode=1 \
./build/test_queue
```
Key output categories:
- `definitely lost` — classic leak (must fix)
- `indirectly lost` — pointer to leaked memory (fix root)
- `possibly lost` — pointer arithmetic made it look lost (review)
- `still reachable` — freed at exit, not a bug
---
## CMake CTest Integration
```cmake
cmake_minimum_required(VERSION 3.20)
project(mylib C)
set(CMAKE_C_STANDARD 11)
add_compile_options(-Wall -Wextra -Wpedantic -Werror)
enable_testing()
# Library under test
add_library(mylib src/queue.c src/user.c)
target_include_directories(mylib PUBLIC src)
# Unity (vendored)
add_library(unity unity/unity.c)
target_include_directories(unity PUBLIC unity)
# Test binary — queue
add_executable(test_queue tests/test_queue.c)
target_link_libraries(test_queue mylib unity)
target_compile_options(test_queue PRIVATE
-fsanitize=address,undefined -g)
target_link_options(test_queue PRIVATE
-fsanitize=address,undefined)
add_test(NAME QueueTests COMMAND test_queue)
# Test binary — user
add_executable(test_user tests/test_user.c)
target_link_libraries(test_user mylib unity)
target_compile_options(test_user PRIVATE
-fsanitize=address,undefined -g)
target_link_options(test_user PRIVATE
-fsanitize=address,undefined)
add_test(NAME UserTests COMMAND test_user)
```
```bash
cmake -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build
ctest --test-dir build -V # verbose output
ctest --test-dir build --output-on-failure
```
---
## Project Structure
```
project/
src/
queue.c
queue.h
user.c
user.h
tests/
test_queue.c
test_user.c
unity/
unity.c
unity.h
unity_internals.h
CMakeLists.txt
Makefile # optional, for simple projects
```
---
## Strategy: ASan in CI, Valgrind Locally
| Tool | Speed | Detection | Usage |
|------|-------|-----------|-------|
| AddressSanitizer | Fast (~2×) | Most heap/stack/UB errors | CI and local |
| Valgrind | Slow (~20×) | Full memory analysis + leaks | Local debugging |
Use ASan by default in CI (fast feedback). Run Valgrind locally when you suspect a leak that ASan misses, or for production pre-release verification.Related Skills
visual-testing
Visual Regression Testing: tool comparison (Chromatic/Percy/Playwright screenshots/BackstopJS), pixel-diff vs AI-based comparison, baseline management, flakiness strategies (masks, tolerances, waitForLoadState), CI integration with GitHub Actions, and Storybook integration.
typescript-testing
TypeScript testing patterns: Vitest for unit/integration, Playwright for E2E, MSW for API mocking, Testing Library for React components. Core TDD methodology for TypeScript/JavaScript projects.
swift-testing
Swift testing patterns: Swift Testing framework (Swift 6+), XCTest for UI tests, async/await test cases, actor testing, Combine testing, and XCUITest for UI automation. TDD for Swift/SwiftUI.
swift-protocol-di-testing
Protocol-based dependency injection for testable Swift code — mock file system, network, and external APIs using focused protocols and Swift Testing.
scala-testing
Scala testing with ScalaTest, MUnit, and ScalaCheck: FunSpec/FlatSpec test structure, property-based testing with forAll, mocking with MockitoSugar, Cats Effect testing with munit-cats-effect (runTest/IOSuite), ZIO Test, Testcontainers-Scala for database integration tests, and CI integration with sbt. Use when writing or reviewing Scala tests.
rust-testing
Rust testing patterns — unit tests with mockall, integration tests with sqlx transactions, HTTP handler testing (axum), benchmarks (criterion), property tests (proptest), fuzzing, and CI with cargo-nextest.
rust-testing-advanced
Advanced Rust testing anti-patterns and corrections — cfg(test) placement, expect() over unwrap(), mockall expectation ordering, executor mixing (#[tokio::test] vs block_on), PgPool isolation with
ruby-testing
RSpec testing patterns for Ruby and Rails — factories, mocks, request specs, feature specs, VCR, and SimpleCov coverage.
r-testing
R testing patterns: testthat 3e with expect_* assertions, snapshot testing, mocking with mockery and httptest2, covr code coverage, lintr static analysis, property-based testing with hedgehog, testing Shiny apps with shinytest2. Use when writing or reviewing R tests.
python-testing
Python testing strategies using pytest, TDD methodology, fixtures, mocking, and parametrization. Core testing fundamentals.
python-testing-advanced
Advanced Python testing — async testing with pytest-asyncio, exception/side-effect testing, test organization, common patterns (API, database, class methods), pytest configuration, and CLI reference. Extends python-testing.
php-testing
PHP testing patterns: PHPUnit 11 with mocks and data providers, Pest v3 with expectations and datasets, Laravel feature/HTTP tests with RefreshDatabase, Symfony WebTestCase, PHPStan static analysis, Infection mutation testing. Use when writing or reviewing PHP tests.