Unit Test
xUnit Principles
  • Write test suite for each unit in the program
  • All test can be executed (automatically) at any time
  • For each program modification all tests must be passed before the modification is regarded as complete - regression testing
  • Test First – implement later!
  • TDD (Test Driven Development) cycle
  • Concepts
  • Assertions, boolean expression that compares expected and actual results
  • Test Case
  • Test Suite
  • Assertion

    // hello.h
    #ifndef HELLO_H
    #define HELLO_H
    
    char * hello();
    
    #endif
    		
    // hello.c
    #include <stdio.h>
    #include "hello.h"
    
    char * hello()
    {
    	return "Hello ";
    }
    		
    // world.h
    #ifndef WORLD_H
    #define WORLD_H
    
    char * world();
    
    #endif
    		
    // world.c
    #include <stdio.h>
    #include "world.h"
    
    char * world()
    {
    	return "World!\n";
    }
    		
    // test.c
    #include <stdio.h>
    #include <assert.h>
    #include <string.h>
    #include "hello.h"
    #include "world.h"
    
    int main()
    {
    	assert(strcmp(hello(), "Hello ") == 0);
    	assert(strcmp(world(), "World!\n") == 0);
    
    	printf("Complete the test sucessfully ...\n");
    
    	return 0;
    }
    		
    // CMakeLists.txt 1
    cmake_minimum_required(VERSION 2.6)
    
    project(c8)
    
    set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/lib)
    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/bin)
    
    include_directories(${PROJECT_SOURCE_DIR}/include)
    
    link_directories(${PROJECT_SOURCE_DIR}/build/lib)
    
    add_subdirectory(src)
    add_subdirectory(test)
    
    set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/install)
    set(CMAKE_C_FLAGS "-O2")
    		
    // CMakeLists.txt 2
    set(CMAKE_MACOSX_RPATH 1)
    add_library(helloworld_lib_shared  SHARED hello.c world.c)
    set_target_properties(helloworld_lib_shared PROPERTIES OUTPUT_NAME "helloworld")
    set_target_properties(helloworld_lib_shared PROPERTIES VERSION 1.2 SOVERSION 1)
    
    INSTALL(TARGETS helloworld_lib_shared
         	LIBRARY DESTINATION lib)
    
    INSTALL(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include)
    		
    // CMakeLists.txt 3
    file(GLOB_RECURSE c_files *.c)
      
    add_executable(mytest ${c_files})
    target_link_libraries(mytest helloworld)
    
    INSTALL(TARGETS mytest
    	RUNTIME DESTINATION bin)
    		
    CUnit
    1. Write functions for tests (and suite init/cleanup if necessary)
    2. Initialize the test registry - CU_initialize_registry()
    3. Add suites to the test registry - CU_add_suite()
    4. Add tests to the suites - CU_add_test()
    5. Run tests using an appropriate interface, e.g. CU_console_run_tests
    6. Cleanup the test registry - CU_cleanup_registry

    // util.h
    #ifndef UTIL_H
    #define UTIL_H
    
    int doubleNum(int);
    
    #endif
    		
    // hello.h
    #ifndef HELLO_H
    #define HELLO_H
    
    char * hello();
    
    #endif
    		
    // test_1.h
    #ifndef TEST_1_H
    #define TEST_1_H
    
    void test_hello();
    
    #endif
    		
    // test_2.h
    #ifndef TEST_2_H
    #define TEST_2_H
    
    void test_doubleNum();
    
    #endif
    		
    // hello.c
    #include "hello.h"
    
    char * hello()
    {
    	return "Hello World!\n";
    }
    		
    // util.c
    #include "util.h"
    
    int doubleNum(int n)
    {
    	return 2*n;
    }
    		
    // test_1.c
    #include <stdio.h>
    #include <string.h>
    #include <CUnit/CUnit.h>
    #include "hello.h"
    
    void test_hello()
    {
    	CU_ASSERT(strcmp(hello(), "Hello World\n") == 0);
    }
    		
    // test_2.c
    #include <stdio.h>
    #include <CUnit/CUnit.h>
    #include "util.h"
    
    void test_doubleNum()
    {
    	CU_ASSERT(doubleNum(10) == 20);
    	CU_ASSERT(doubleNum(0) == 0);
    	CU_ASSERT(doubleNum(-1) == -2);
    }
    		
    // test.c
    #include <stdio.h>
    #include <string.h>
    #include <CUnit/CUnit.h>
    #include "CUnit/Basic.h"
    #include "CUnit/Automated.h"
    #include "hello.h"
    #include "util.h"
    #include "test_1.h"
    #include "test_2.h"
    
    int suitInit() {return 0;}
    int suitClean() {return 0;}
    
    int main()
    {
    	// initialize registry
    	if (CUE_SUCCESS != CU_initialize_registry())
    		return CU_get_error();
    
    	// add a suit to the registry
    	CU_pSuite pSuite = NULL;
    	pSuite = CU_add_suite("Suite_1", suitInit, suitClean);
    	if (NULL == pSuite) {
    	  	CU_cleanup_registry();
    	  	return CU_get_error();
         	}
    
    	// add tests to the suit
    	if ((NULL == CU_add_test(pSuite, "test of hello()", test_hello)) || 
    			(NULL == CU_add_test(pSuite, "test of hello()", test_doubleNum)))
       	{
          		CU_cleanup_registry();
          		return CU_get_error();
       	}
    
    	// run all tests using the CUnit Basic interface, output report to stdout
       	CU_basic_set_mode(CU_BRM_VERBOSE);
       	CU_basic_run_tests();
    
    	// run all tests using the automated interface, output report to xml files
       	CU_automated_run_tests();
       	CU_list_tests_to_file();
    
    	// clean registry
       	CU_cleanup_registry();
       	return CU_get_error();
    }
    		
    // CMakeLists.txt 1
    cmake_minimum_required(VERSION 2.6)
    
    project(unittest)
    
    set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/lib)
    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/bin)
    
    include_directories(${PROJECT_SOURCE_DIR}/include /usr/local/include)
    
    link_directories(${PROJECT_SOURCE_DIR}/build/lib)
    
    add_subdirectory(src)
    add_subdirectory(test)
    
    set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/install)
    set(CMAKE_C_FLAGS "-O2")
    		
    // CMakeLists.txt 2
    set(CMAKE_MACOSX_RPATH 1)
    add_library(unittest_lib_shared  SHARED hello.c util.c)
    set_target_properties(unittest_lib_shared PROPERTIES OUTPUT_NAME "unittest")
    set_target_properties(unittest_lib_shared PROPERTIES VERSION 1.2 SOVERSION 1)
    
    INSTALL(TARGETS unittest_lib_shared
         	LIBRARY DESTINATION lib)
    
    INSTALL(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include)
    		
    file(GLOB_RECURSE c_files *.c)
      
    add_executable(my_unit_test ${c_files})
    find_library(TEST_LIBRARY cunit HINTS /usr/local/lib)
    message("Test: " ${TEST_LIBRARY})
    target_link_libraries(my_unit_test ${TEST_LIBRARY} unittest)
    
    INSTALL(TARGETS my_unit_test
    	RUNTIME DESTINATION bin)
    		
    Functions
  • CU_initialize_registry(), initialize CUnit test
  • CU_cleanup_registry(), clean up and release memory used by the framework
  • CU_add_suite(name, initialization function, cleanup function)
  • CU_add_test(suit, name, test function)
  • CU_get_error()
  • CU_BasicRunMode()
  • CU_basic_run_tests(), runs all tests in all registered suites
  • CU_basic_run_suite(CU_pSuite pSuite), run tests in a specific suit
  • CU_basic_run_test(CU_pSuite pSuite, CU_pTest pTest), runs a single test in a specified suite
  • CU_basic_show_failures(), print a summary of run failures to stdout
  • CU_get_failure_list(), return a linked list of failures
  • CU_automated_run_tests(), runs all tests in all registered suites, generate xml files
  • CU_list_tests_to_file(), lists the registered suites and associated tests to file
  • CU_console_run_tests(), the console interface is interactive
  • Reference
  • CUnit
  • C Language Unit Test