以md5测试为例子,练习了cunit这个单元测试工具。

要写一个测试用例,只需要遵循用例的框架编写就好了。

我们应该要清楚自己编写的函数的输入是什么,输出是什么。然后使用cunit的断言看结果是否符合预期,是的话测试通过,否则说明编码有问题。

就这次将要编写的md5为例,需要测试两个函数,一个是输入字符串,输出计算后的md5值,另一个是输入文件路径,输出文件的md5值。所以用例是否通过的条件就是计算的md5值是否正确。

先来看看main函数

:::c
int main(int argc, const char *argv[])
{
    return run_test();
}

没什么特别的,就是运行了run_test这个函数。

再来看看run_test

:::c
int run_test()
{
    if(CU_initialize_registry())
    {
        exit(EXIT_FAILURE);
    }
    else
    {
        AddTests();

        /**** Automated Mode *****************
          CU_set_output_filename("TestMax");
          CU_list_tests_to_file();
          CU_automated_run_tests();
        //************************************/

        /*********basic mode**************/
//		CU_basic_set_mode(CU_BRM_VERBOSE);
//		CU_basic_run_tests();

        /*********console mode**************/
        CU_console_run_tests();

        CU_cleanup_registry();
        return CU_get_error();
    }
}

开始变得有趣了,我们看到运行cunit,先要执行CU_initialize_registry(),成功后运行我们写的AddTests添加测试用例,接着运行测试模式,这里有三种,我用了第三种控制台模式。最后return退出。

接着来看看我们的AddTests做了什么工作。

:::c
void AddTests()
{
    assert(NULL != CU_get_registry());
    assert(!CU_is_test_running());

    if(CUE_SUCCESS != CU_register_suites(suites))
    {
        exit(EXIT_FAILURE);
    }
}

原来AddTests的动作就是注册suites。那么什么是suites?

:::c
CU_TestInfo testcase[] = {
    {"test for md5", test_md5_null},
    {"test for md5 a", test_md5_a},
    {"test for md5 letter", test_md5_letter},
    CU_TEST_INFO_NULL
};

CU_TestInfo testcase2[] = {
    {"test for md5 file", test_md5_file},
    CU_TEST_INFO_NULL
};

int suite_success_init(void)
{
    return 0;
}

int suite_success_clean(void)
{
    return 0;
}

CU_SuiteInfo suites[] = {
    {"test suite 1", suite_success_init, suite_success_clean,testcase},
    {"test suite 2", suite_success_init, suite_success_clean,testcase2},
    CU_SUITE_INFO_NULL
};

从下往上看,suites 其实就是记录SuiteInfo的结构体,这里有两个suite,分别是suite1和suite2,而suite1中包含三个测试用例,suite2中包含一个。

最终我们就是要编写这些测试用例来完成测试。

例如test_md5_null,是用来测试null时计算到的md5值是否正确的。

:::c
void test_md5_null(void)
{
    char* target;
    target = (char* )malloc((MD5_DIGEST_LENGTH * 2 + 1) * sizeof(char));
    md5_string("", target);

    printf("target is %s \n", target);
    CU_ASSERT_STRING_EQUAL(target, "d41d8cd98f00b204e9800998ecf8427e");
    free(target);
}

我们知道null的正确md5值是d41d8cd98f00b204e9800998ecf8427e,计算结果是target,使用CU_ASSERT_STRING_EQUAL来判断结果是否相等。

流程大致上就这样。写了个简单的Makefile

:::sh
INC = -I/usr/local/include/CUnit
LIB = -L/usr/local/lib/ 

all:md5c.c md5test.c 
    gcc -o test $(INC) $(LIB) $^ -lcunit -static 

clean:
    rm test

开始检测字符串时全军覆没。经检测发现是数据类型用错了,在64位系统下,long int是8个字节,而其要求一个4字节的数据类型。

测试结果如下:

:::sh
Running Suite : test suite 1
     Running Test : test for md5input src  size 0 
d41d8cd98f00b204e9800998ecf8427e
target is d41d8cd98f00b204e9800998ecf8427e 

     Running Test : test for md5 ainput src a size 1 
0cc175b9c0f1b6a831c399e269772661
target is 0cc175b9c0f1b6a831c399e269772661 

     Running Test : test for md5 letterinput src abcdefghijklmnopqrstuvwxyz size 26 
c3fcd3d76192e4007dfb496cca67e13b
target is c3fcd3d76192e4007dfb496cca67e13b 

Running Suite : test suite 2
     Running Test : test for md5 fileopen file error 


Run Summary:    Type  Total    Ran Passed Failed Inactive
              suites      2      2    n/a      0        0
               tests      4      4      4      0        0
             asserts      3      3      3      0      n/a

Elapsed time =    0.000 seconds