Expert · Debugging and tools · topic 54 of 60 · 25 min
assert, logging and testing C code
Explanation
assert: checking your own assumptions
#include <assert.h>
double average(const int *marks, int n) {
assert(marks != NULL); /* a programming error if this is false */
assert(n > 0);
long sum = 0;
for (int i = 0; i < n; i++) sum += marks[i];
return (double) sum / n;
}
/* if violated: prog: report.c:5: average: Assertion 'n > 0' failed. (then abort) */An assertion documents and checks something that must ALWAYS be true if the code is correct. When it fails, the program stops immediately at the real cause, instead of crashing later in a confusing place. Compiling with -DNDEBUG removes every assert, so release builds pay nothing.
Error codes and errno
#include <errno.h>
#include <string.h>
FILE *f = fopen("config.ini", "r");
if (!f) {
fprintf(stderr, "cannot open config.ini: %s\n", strerror(errno)); /* e.g. "No such file or directory" */
return -1;
}A common C convention: functions return 0 on success and a negative number on failure, and put results in output pointers. Document each function's error codes, and check them at every call.
A tiny test harness
#include <stdio.h>
static int failures = 0;
#define CHECK(cond) do { \
if (!(cond)) { fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); failures++; } \
} while (0)
int gcd(int a, int b);
int main(void) {
CHECK(gcd(84, 36) == 12);
CHECK(gcd(17, 5) == 1);
CHECK(gcd(10, 0) == 10); /* edge case */
CHECK(gcd(0, 0) == 0); /* edge case: what SHOULD it return? */
if (failures) { printf("%d test(s) failed\n", failures); return 1; }
printf("all tests passed\n");
return 0;
}Test frameworks
Larger projects use a framework that organises tests, reports results and helps with mocks: Unity (popular in embedded work, a single C file), CMocka (with mocking support), Check, or GoogleTest (C++, but often used to test C libraries). The ideas are the same as in any language: arrange, act, assert, one behaviour per test, edge cases first.
What to test in C code
- Boundaries: 0, 1, the maximum size, exactly-full buffers, empty strings.
- Error paths: allocation failure (inject it), missing files, invalid input.
- Round trips: write then read a file; encode then decode.
- Run the whole test suite under sanitizers (topic 3).
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
assert, logging and testing C code · 1/6
In this lesson you will check assumptions with assert and test C code.
Prefer a video by a teacher? Find free videos on “c assert unit testing unity cmocka tutorial” on YouTube ↗
In real life: Unit tests for a smart-meter tariff module
An energy company's meter firmware has a tariff module that turns units into a bill with slabs. The team keeps 40 Unity tests for it, including the slab boundaries (exactly 100 and 101 units) and zero consumption, and runs them on a PC on every commit, long before flashing a device.
void test_slab_boundary(void) {
TEST_ASSERT_EQUAL_INT(300, bill_paise_units(100) / 100); /* 100 units × Rs 3 */
TEST_ASSERT_EQUAL_INT(305, bill_paise_units(101) / 100); /* +1 unit at Rs 5 */
}Practice (0/3 done)
What happens to assert(x > 0) when the program is compiled with -DNDEBUG?
Build the same kind of tiny test harness in JavaScript, which runs here: write makeHarness() returning { check(cond, name), summary() }. check records a failure (by name) when cond is false; summary() returns { passed, failed, failures } where failures is the list of failed names in order.
For your linked-list or hash-table code from module 10, write a test file with a CHECK macro (or Unity) containing at least 10 tests: normal cases, empty structures, removing missing items, and freeing. Add asserts for internal assumptions (non-NULL arguments). Run the tests normally, with -DNDEBUG, and under -fsanitize=address,undefined.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- A method for debuggingDebugging and tools · same module
- Finding memory bugs with valgrindDebugging and tools · same module
- Unit testsRust Programming · Testing, tooling and unsafe Rust
- Unit testing: checking one piece at a timeBackend Development · Testing and API documentation
- Testing your codePython Programming · Algorithms, data structures and testing
- Unit testing with xUnitC# and .NET · Testing, tooling and Unity
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What should assert be used for?
Answer: Checking conditions that must be true if the code is correct
Programmer errors, not user errors.
Why is assert(fclose(f) == 0) a bug?
Answer: With NDEBUG the fclose call disappears
Side effects vanish in release builds.
What does strerror(errno) return?
Answer: A text description of the last error
For example "Permission denied".
Which is a C unit-testing framework popular in embedded work?
Answer: Unity
Unity is a small C framework.
What does assert_eq! need from the compared type?Related
In Arrange, Act, Assert, what happens in the Act step?Related
Answer: You call the code being tested
Arrange prepares, Act calls the function once, Assert checks the result.
Why should real programs not use assert to validate user input?Related
Answer: Running Python with -O removes all asserts, so the checks vanish
Optimised mode (-O) strips assert statements. Use proper exceptions like ValueError for input checks.
From Testing your code
What does Arrange-Act-Assert describe?Related
Answer: The structure of a test: set up, call, check
It keeps tests readable.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “Debugging and tools” before its exam: A method for debugging.
- Module 11 exam: Debugging and tools · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Embedded C, system calls and careers. 60 topics left in the course before the final exam.