Check

Description

Check is a simple and minimalistic unit testing framework for C. It’s easy to learn and easy to install.

Setup

Most of us will get away with either
apt-get install check
or
yum install check.

The simplest possible Makefile would look something like this:

CC=gcc
CFLAGS=-c -Wall
LDFLAGS=-lcheck -lrt -lpthread -lm

all: run_tests

run_tests: run_tests.o my_tests.o
    $(CC) $^ -o $@ $(LDFLAGS)

.c.o:
    $(CC) $(CFLAGS) $< -o $@

clean:
    rm -f *.o *~ run_tests

Functionality

Check is really small, so this section is quite comprehensive.

Test Organization

Tests are grouped into suites, which are divided into test cases.  A test case may contain multiple tests.  I find the grouping into test cases the least useful; mainly because it’s only made visible for failing tests.

#include <check.h>

START_TEST (sample_test)
{
   ck_assert_int_eq(42, 42);
}
END_TEST

Suite *create_sample_suite(void)
{
    Suite *suite = suite_create("Sample suite");
    TCase *test_case = tcase_create("Sample test case");
    tcase_add_test(test_case, sample_test);
    return suite;
}

To run the tests, I always use the same file. The header file tests.h contains definitions of all test suite names.

#include <check.h>
#include <stdlib.h>
#include "tests.h"

int main(void)
{
   int number_failed;
   SRunner *runner = srunner_create(create_sample_suite());
   srunner_add_suite(runner, <another suite would go here>);
   srunner_run_all(runner, CK_NORMAL);
   number_failed = srunner_ntests_failed(runner);
   srunner_free(runner);
   return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Integer Assertions

#include <check.h>

START_TEST (int_eq)
{
    ck_assert_int_eq(42, 42);
}
END_TEST

START_TEST (int_ne)
{
    ck_assert_int_ne(42, 21);
}
END_TEST

START_TEST (int_lt)
{
    ck_assert_int_lt(41, 42);
}
END_TEST

START_TEST (int_le)
{
    ck_assert_int_le(41, 42);
    ck_assert_int_le(42, 42);
}
END_TEST

START_TEST (int_gt)
{
    ck_assert_int_gt(43, 42);
}
END_TEST

START_TEST (int_ge)
{
    ck_assert_int_ge(42, 42);
    ck_assert_int_ge(43, 42);
}
END_TEST

Suite *create_integer_assertions_suite(void)
{
    Suite *suite = suite_create("Integer assertions");
    TCase *eq_case = tcase_create("Equality");
    tcase_add_test(eq_case, int_eq);
    tcase_add_test(eq_case, int_ne);

    TCase *ltgt_case = tcase_create("Less than/Greater than");
    tcase_add_test(ltgt_case, int_lt);
    tcase_add_test(ltgt_case, int_le);
    tcase_add_test(ltgt_case, int_gt);
    tcase_add_test(ltgt_case, int_ge);
    suite_add_tcase(suite, ltgt_case);
    return suite;
}

String Assertions

I’ve omitted the suite creation here for brevity.

#include <check.h>

START_TEST (str_eq)
{
   ck_assert_str_eq("Hello World", "Hello World");
}
END_TEST

START_TEST (str_ne)
{
   ck_assert_str_ne("Hello Check", "Hello World");
}
END_TEST

START_TEST (str_lt)
{
   ck_assert_str_lt("Hello Check", "Hello World");
}
END_TEST

START_TEST (str_le)
{
   ck_assert_str_le("Hello Vorld", "Hello World");
   ck_assert_str_le("Hello World", "Hello World");
}
END_TEST

START_TEST (str_gt)
{
    ck_assert_str_gt("Hello Xorld", "Hello World");
}
END_TEST

START_TEST (str_ge)
{
    ck_assert_str_ge("Hello World", "Hello World");
    ck_assert_str_ge("Hello Xorld", "Hello World");
}
END_TEST

General Functions

Again, suite omitted for brevity.

#include <check.h>
#include <stdbool.h>

START_TEST (general_assertion)
{
   ck_assert(true == true);
   ck_assert(1 == 2 - 1);
   ck_assert(strcmp("hello", "Hello") > 0);
}
END_TEST

START_TEST (pointer_equality)
{
   char *p1 = "Hello World";
   char *p2 = p1;
   ck_assert_ptr_eq(p1, p2);
}
END_TEST

START_TEST (pointer_inequality)
{
   char *p1 = "Hello World";
   char *p2 = p1;
   p2++;
   ck_assert_ptr_ne(p1, p2);
}
END_TEST


START_TEST (general_assertion_with_message)
{
   ck_assert_msg(true == 0, "True is not 0");
}
END_TEST

START_TEST (abort_test)
{
   ck_abort();
}
END_TEST

START_TEST (abort_test_with_message)
{
   ck_abort_msg("I fail, and tell you that I do");
}
END_TEST

Reporting

If you don’t want just the default textual output, setting the environment variable CK_XML_LOG_FILE_NAME may prove quite helpful. For instance, the xUnit plugin for Jenkins will be able to publish the results.

stage('Test') {
    env.CK_XML_LOG_FILE_NAME = 'TEST-results.xml'
    sh './run_tests'
    step([$class: 'XUnitPublisher', tools: [
        [$class: 'CheckType', pattern: '**/TEST-*.xml', stopProcessingIfError: true]
    ]])
}