Unit testing C with Check
On my Lubuntu laptop, I installed Check (sometimes referred to as
libcheck
) via
sudo apt install check
I don't know exactly what that did but I see this
/usr/include/check.h
and
/usr/include/subunit/child.h
I didn't see it immediately when on the Check homepage but the manual is available by clicking on the Reference tab and it's helpful even though the example given is more involved than a "Hello, world" program. Below is a simpler example.
Testing an add
function
I created a toy add
function to test
int add(int a, int b) { return a + b; }
and a header file for add
.
int add(int a, int b);
I next created the object file called add.o
.
gcc -c -Wall add.c
I created this untested "application" which uses my add
function:
#include <stdio.h> #include "add.h" int main(void) { int a=1; int b=3; int c = add(a, b); printf("%d plus %d is %d\n", a, b, c); return 0; }
and built the binary:
gcc -Wall add.o app.c -o app ./app
All the code to test add
is saved in the file test_add.c
which
includes check.h
and add.h
. I started by defining the test which
I named mytest
. The START_TEST
macro takes the name of the test
as an argument. (The END_TEST
macro finishes the test.) I use
Check's ck_assert_int_eq
function (or maybe it's a macro). Within
mytest
I can have any number of assertions.
#include <stdlib.h> #include <check.h> #include "add.h" START_TEST(mytest) { ck_assert_int_eq(add(3, 9), 12); } END_TEST
Check wants me to next create a test suite. I created a function called
mysuite
which returns a pointer to Suite. (This code is also in
test_add.c
.)
Suite *mysuite(void) { /* Boilerplate */ Suite *s; TCase *tc_core; /* Create the test suite which I call 'Add' */ s = suite_create("Add"); /* Core test case [seems always needed?] */ tc_core = tcase_create("Core"); /* add my test to 'tc_core'? */ tcase_add_test(tc_core, mytest); /* put my tests into the suite? */ suite_add_tcase(s, tc_core); return s; }
The last part is my main function which is also in test_add.c
.
int main(void) { int number_failed; Suite *s; SRunner *sr; s = mysuite(); /* <-- only line specific to my test? */ sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }
I then compiled test_add.c
using the following (which involved some
trial and error):
gcc test_add.c add.o -lcheck -lsubunit -lm -o test_add
A Makefile
Here is how to build test_add
which depends on add.o
and how to
clean up and run the tests.
add.o: add.c add.h gcc -c -Wall add.c test_add: test_add.c add.o gcc test_add.c add.o -lcheck -lsubunit -lm -o test_add PHONY: clean clean: rm add.o test_add PHONY: test test: test_add ./test_add
make -f Makefile.check clean make -f Makefile.check test
A full 100% of the tests in my Add
test suite are passing.