Test C Programs with Cases, Warnings and Assertions
Test C Programs with Cases, Warnings and Assertions
A pass-checking function works for 45, but an off-by-one error could still reject exactly 40. Tests should distinguish the rule’s boundary, not just exercise its easiest input.
A useful test compares an observed result with an independently stated requirement. Normal cases, boundaries and valid failure paths expose different kinds of mistakes.
Explain
Choose cases that distinguish >= 40 from > 40.
Trace
Add checks for the valid endpoint marks zero and one hundred.
Apply
Choose boundary cases rather than testing only the happy path.
Test C Programs: follow the data
Start
A pass-checking function works for 45, but an off-by-one error could still reject exactly 40. Tests should distinguish the rule’s boundary, not just exercise its easiest input.
Operation
A useful test compares an observed result with an independently stated requirement. Normal cases, boundaries and valid failure paths expose different kinds of mistakes.
Result
Threshold checks passed
Understand the operation
For a valid mark between zero and one hundred, check 39, 40 and 41. The middle case identifies whether the threshold is inclusive. Add the endpoints zero and one hundred to cover the valid range.
assert checks an expression when assertions are enabled. It is a development check, not input validation: defining NDEBUG can remove the check. Never hide essential work inside an assertion expression.
Read the complete C program
This complete C17 program uses a small school example. Read declarations before the statements that use their values. Each printf format describes how the corresponding value is written to standard output.
C17 · complete program
#include <stdio.h>
#include <assert.h>
static int passed(int mark) { return mark >= 40; }
int main(void) {
assert(!passed(39));
assert(passed(40));
assert(passed(41));
puts("Threshold checks passed");
return 0;
}
Expected output
Threshold checks passed
Run in C compiler →Follow the values through the code
C17 · code used in this explanation
#include <stdio.h>
#include <assert.h>
static int passed(int mark) { return mark >= 40; }
int main(void) {
assert(!passed(39));
assert(passed(40));
assert(passed(41));
puts("Threshold checks passed");
return 0;
}
The first assertion expects a below-threshold mark not to pass.
The next two verify the exact threshold and a value above it. The message is printed after all active assertions succeed.
Follow the worked calculation
Read the starting values
The first assertion expects a below-threshold mark not to pass.
Perform the operation
The next two verify the exact threshold and a value above it. The message is printed after all active assertions succeed.
Check the observable result
Threshold checks passed
Threshold checks passed
Try a small variation
C17 · code used in this explanation
#include <stdio.h>
#include <assert.h>
static int passed(int mark) { return mark >= 40; }
int main(void) {
assert(!passed(39));
assert(passed(40));
assert(passed(41));
puts("Threshold checks passed");
return 0;
}
Add checks for the valid endpoint marks zero and one hundred.
Worked answer
The additional assertions test two valid endpoints. They do not replace the boundary case at forty, which is the case that distinguishes the common off-by-one implementations.
C17 · complete program
#include <stdio.h>
#include <assert.h>
static int passed(int mark) { return mark >= 40; }
int main(void) {
assert(!passed(0));
assert(!passed(39));
assert(passed(40));
assert(passed(100));
puts("Endpoint checks passed");
return 0;
}
Expected output
Endpoint checks passed
Run in C compiler →Recognize the important boundaries
Tests cover the cases you execute; they do not prove all inputs safe. Compiler diagnostics and memory/undefined-behavior sanitizers provide complementary evidence.
Validate external input with ordinary control flow. An assertion may be disabled and must not be used as the only protection against an invalid index.
Check your understanding
Choose the answer, then compare the reasoning for every option.
Use the idea independently
A useful test compares an observed result with an independently stated requirement. Normal cases, boundaries and valid failure paths expose different kinds of mistakes.
Continue with the practice-program index.
Language reference: WG14 C11 committee draft. These examples use the C17-compatible subset of the explained language features.
Reaching the recap and completing the quizzes are separate milestones.