Skip to content
Tony Bai edited this page Sep 16, 2013 · 4 revisions

Table of Contents

Overview

lcut is short for "Lightweight C Unit Test framework". it is implemented in standard C and easy to be used in different mainstream platform, such as solaris, linux and so on.

Test executions

A unit test using lcut is divided into three levels: logical test, test suite and test case. A logical test contains several test suites and each test suite also contains several test cases. test case is the most basic and the smallest unit in this framework.

Actually the test cases in lcut are functions with the signature

void (*tc_func)(lcut_tc_t *tc, void *data)
. lcut use LCUT_TC_ADD macro to add a test case into a test suite initiallized by invoking the macro LCUT_TS_INIT. Each unit test in lcut should start with LCUT_TEST_BEGIN and end with LCUT_TEST_END. LCUT_TEST_RESULT is used to tell the unit test invoker(such as a continuous integration script and so on) the result of this test execution.
#include "lcut.h"

/*
 * A case that test nothing
 */
void tc_trivial_test(lcut_tc_t *tc, void *data) {

}

int main() {
    lcut_ts_t   *suite = NULL;
    LCUT_TEST_BEGIN("A Trivial test", NULL, NULL);

    LCUT_TS_INIT(suite, "A Trivial test suite", NULL, NULL);
    LCUT_TC_ADD(suite, "A Trivial test case", tc_trivial_test, NULL, NULL, NULL);
    LCUT_TS_ADD(suite);

    LCUT_TEST_RUN();
    LCUT_TEST_REPORT();
    LCUT_TEST_END();

    LCUT_TEST_RESULT();
}

The output of this test execution are as below:

*********************************************************
    LCUT -- Lightweight C Unit Testing framework
          By Tony Bai
*********************************************************
Unit Test for 'A Trivial test':

    Suite <A Trivial test suite>:
        Case 'A Trivial test case': Passed

Summary:
    Total Suites: 1
    Failed Suites: 0
    Total Cases: 1
    Failed Cases: 0

   ==========================
          GREEN BAR!
   ==========================

if there are no test case failed, "GREEN BAR!" will be displayed on your console in green color, Otherwise "RED BAR!" will be present and the failed cases will be listed with the failed reasons, which is as below:

Unit Test for 'A Trivial test':

    Suite <A Trivial test suite>:
        Case 'A Trivial test case': Failure occur in tc_null_test, 23 line in file runtests.c, expected<1> : actual<2>

Summary:
    Total Suites: 1
    Failed Suites: 0
    Total Cases: 1
    Failed Cases: 0

   ==========================
        RED BAR!
   ==========================

Assertions

lcut provides several assertion macros used in your unit test.

  • LCUT_INT_EQUAL(tc, expected, actual)
  • LCUT_INT_NEQUAL(tc, expected, actual)
  • LCUT_STR_EQUAL(tc, expected, actual)
  • LCUT_STR_NEQUAL(tc, expected, actual)
  • LCUT_ASSERT(tc, msg, condition)
  • LCUT_TRUE(tc, condition)
The usage of these macros are extremely obvious.

Mock support

lcut support mock which is an important way to isolate the unit test from any external dependencies. LCUT_MOCK_RETV and LCUT_MOCK_ARG are separatedly used to mock the return value and the output arguments of functions being mocked.

a mocked function 'table_row_count' could be implemented as below:

int table_row_count(const database_conn *conn,
                    const char *table_name,
                    int *total_count) {
    (*total_count) = (int)LCUT_MOCK_ARG();
    return (int)LCUT_MOCK_RETV();
}

the test case having dependency on 'table_row_count' could use the mocked 'table_row_count' and use LCUT_RETV_RETURN and LCUT_ARG_RETURN to control the return value and output parameter freely.

    LCUT_ARG_RETURN(table_row_count, 5);  /* the total_count will be 5 after invoking the mocked table_row_count */
    LCUT_RETV_RETURN(table_row_count, 0); /* the return value of mocked table_row_count will be 0 */

lcut also supply the other two macros LCUT_ARG_RETURN_COUNT and LCUT_RETV_RETURN_COUNT ,both of which are used in the situation when a function invokes the mocked interface multiple times:

for example:

int bar(int *outparameter) {
   ...
   foo();

   ...
   foo();

   ...
   foo();
}

if we want to test bar, we have to mock foo. but here we must have to set the return value and out parameter of mocked foo for three times by using LCUT_RETV_RETURN and LCUT_ARG_RETURN. But if we use LCUT_ARG_RETURN_COUNT and LCUT_RETV_RETURN_COUNT, we just need to set once. for example:

/* in some test case */
  LCUT_RETV_RETURN_COUNT(foo, 13, 3);
  LCUT_ARG_RETURN_COUNT(foo, 17, 3);

if you want the mocked foo always return one same value, you can pass -1 as the count argument, for example:

LCUT_RETV_RETURN_COUNT(foo, 13, -1);/* make foo always return 13 */
LCUT_ARG_RETURN_COUNT(foo, 17, -1); /* make foo's outparameter always evaluated to 17 */

Further details about mock support are in the example directory in lcut package.

Note: lcut doesn't support mock the return values and parameter which has the type 'double'.

Clone this wiki locally