【单元测试】CUnit单元测试框架(不支持mock功能)

1 CUnit简介

CUnit是一个通过C语言编写单元测试框架,用于编写、管理和执行单元测试用例的测试系统。

  • 官网:http://cunit.sourceforge.net/
  • 帮助文档:http://cunit.sourceforge.net/documentation.html

有关CUnit框架的更多介绍,可以参阅CUnit用户手册:

  • 官方英文版:http://cunit.sourceforge.net/doc/index.html
  • 中文翻译版:https://blog.csdn.net/benkaoya/article/details/95887879

本文重点讲解如何在Linux环境下使用CUnit框架实现单元测试。

2 下载CUnit

下载地址:CUnit-2.1-3.tar.bz2

这是截止书稿本文时最新的版本。

3 编译CUnit

3.1 我的环境

# uname -vom
#35-Ubuntu SMP Fri Aug 10 17:58:07 UTC 2018 x86_64 GNU/Linux

# lsb_release -d
Description:    Ubuntu 18.04.2 LTS

3.2 编译安装

# tar -jxvf CUnit-2.1-3.tar.bz2
# cd CUnit-2.1-3
# mv configure.in configure.ac
# aclocal
# autoconf
# autoheader
# libtoolize
# automake --add-missing
# automake
# mkdir <install directory>
# ./configure --prefix <install directory>
# make
# make install

以下是我编译后的结果:

# tree -L 3 /opt/CUnit
/opt/CUnit
├── doc
│   └── CUnit
│       ├── CUnit_doc.css
│       ├── error_handling.html
│       ├── fdl.html
│       ├── headers
│       ├── index.html
│       ├── introduction.html
│       ├── managing_tests.html
│       ├── running_tests.html
│       ├── test_registry.html
│       └── writing_tests.html
├── include
│   └── CUnit
│       ├── Automated.h
│       ├── Basic.h
│       ├── Console.h
│       ├── CUError.h
│       ├── CUnit.h
│       ├── CUnit_intl.h
│       ├── MyMem.h
│       ├── TestDB.h
│       ├── TestRun.h
│       └── Util.h
├── lib
│   ├── libcunit.a
│   ├── libcunit.la
│   ├── libcunit.so -> libcunit.so.1.0.1
│   ├── libcunit.so.1 -> libcunit.so.1.0.1
│   ├── libcunit.so.1.0.1
│   └── pkgconfig
│       └── cunit.pc
└── share
    ├── CUnit
    │   ├── CUnit-List.dtd
    │   ├── CUnit-List.xsl
    │   ├── CUnit-Run.dtd
    │   ├── CUnit-Run.xsl
    │   ├── Memory-Dump.dtd
    │   └── Memory-Dump.xsl
    └── man
        └── man3

11 directories, 31 files

4 开始使用

4.1 示例1

写一个简单的demo进行演示,使用CUnit框架对sum求和函数(内部故意安装一个BUG)进行单元测试,如下代码所示(文件名为example1.c):

#include <stdio.h>
#include <string.h>
#include "CUnit/Basic.h"

/* 被测试的函数,在当中故意安装了一个BUG */
static int sum(int a, int b)
{
    if (a > 4) {
        return 0;
    }
    return (a + b);
}

static int suite_init(void)
{
    return 0;
}

static int suite_clean(void)
{
    return 0;
}

static void test_sum(void)
{
    CU_ASSERT_EQUAL(sum(1, 2), 3);
    CU_ASSERT_EQUAL(sum(5, 2), 7);
}

int main()
{
    CU_pSuite pSuite = NULL;

    /* initialize the CUnit test registry */
    if (CUE_SUCCESS != CU_initialize_registry()) {
        return CU_get_error();
    }

    /* add a suite to the registry */
    pSuite = CU_add_suite("suite_sum", suite_init, suite_clean);
    if (NULL == pSuite) {
        CU_cleanup_registry();
        return CU_get_error();
    }

    /* add the tests to the suite */
    if ((NULL == CU_add_test(pSuite, "test_sum", test_sum))) {
        CU_cleanup_registry();
        return CU_get_error();
    }

    /* Run all tests using the CUnit Basic interface */
    CU_basic_set_mode(CU_BRM_VERBOSE);
    CU_basic_run_tests();

    /* Clean up registry and return */
    CU_cleanup_registry();
    return CU_get_error();
}

编译:

# gcc -o test example1.c -I/opt/CUnit/include /opt/CUnit/lib/libcunit.a

运行结果:

# ./test 


     CUnit - A unit testing framework for C - Version 2.1-3
     http://cunit.sourceforge.net/


Suite: suite_sum
  Test: test_sum ...FAILED
    1. example1.c:29  - CU_ASSERT_EQUAL(sum(5, 2),7)

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

Elapsed time =    0.000 seconds

测试结果一目了然,执行失败的测试用例也罗列出来,很直观。

4.2 示例2

除了示例1的代码,同样的功能,还可以使用 CU_register_suites 函数代替 CU_add_suite 和 CU_add_test 接口,如下示例2所示,大家可以自由选择使用哪种方式,殊途同归:

#include <stdio.h>
#include <string.h>
#include "CUnit/Basic.h"

/* 被测试的函数,在当中故意安装了一个BUG */
static int sum(int a, int b)
{
    if (a > 4) {
        return 0;
    }
    return (a + b);
}

static int suite_init(void)
{
    return 0;
}

static int suite_clean(void)
{
    return 0;
}

static void test_sum(void)
{
    CU_ASSERT_EQUAL(sum(1, 2), 3);
    CU_ASSERT_EQUAL(sum(5, 2), 7);
}

static CU_TestInfo tests_sum[] = {
    { "test_sum", test_sum },
    CU_TEST_INFO_NULL,
};

static CU_SuiteInfo suites[] = {
  { "suite_sum", suite_init, suite_clean, NULL, NULL, tests_sum },
  CU_SUITE_INFO_NULL,
};

int main()
{
    CU_pSuite pSuite = NULL;

    /* initialize the CUnit test registry */
    if (CUE_SUCCESS != CU_initialize_registry()) {
        return CU_get_error();
    }

    /* Register suites. */
    if (CUE_SUCCESS != CU_register_suites(suites)) {
        CU_cleanup_registry();
        return CU_get_error();
    }

    /* Run all tests using the CUnit Basic interface */
    CU_basic_set_mode(CU_BRM_VERBOSE);
    CU_basic_run_tests();

    /* Clean up registry and return */
    CU_cleanup_registry();
    return CU_get_error();
}

4.3 四种模式输出

CUnit执行结果输出支持四种模式,分别为:

模式 支持的平台 执行结果输出方式 备注
Basic All 输出到标准输出 最常用的,结果输出到标准输出(stdout)
Automated All 输出到xml文件 生成完XML文件之后,然后再将CUnit-List.dtd、CUnit-List.xsl、CUnit-Run.dtd、CUnit-Run.xsl(这几个文件在CUnit的源码包可以找到)和XML文件放到同一级目录,再用IE浏览器打开,就可以看到漂亮的界面了。
Console All 交互式控制台方式 比较灵活,可以选择只执行其中某一个测试用例。
Curses Linux/Unix 交互式curses窗口方式 跟Console类似,只不过是以Curses窗口的方式展示。

前面示例展示的就是Basic模式,在CUnit官网的 Screenshots 中可以看到各个模式的效果图。

在代码中要使用不同模式,只要调用相应的接口函数即可(也可以同时使用多种模式):

模式 使用的接口函数
Basic #include "CUnit/Basic.h"

CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
Automated #include "CUnit/Automated.h"

CU_list_tests_to_file();
CU_automated_run_tests();
Console #include "CUnit/Console.h"

CU_console_run_tests();
Curses #include "CUnit/CUCurses.h"

CU_curses_run_tests();

4.4 CUnit断言

断言是单元测试中重要的组成部分,CUnit的断言定义在 #include <CUnit/CUnit.h> 头文件中,在 CUnit-2.1-3 版本中,所有的断言如下表所示:

断言 描述
CU_ASSERT(int expression)
CU_ASSERT_FATAL(int expression)
CU_TEST(int expression)
CU_TEST_FATAL(int expression)
Assert that expression is TRUE (non-zero)
CU_ASSERT_TRUE(value)
CU_ASSERT_TRUE_FATAL(value)
Assert that value is TRUE (non-zero)
CU_ASSERT_FALSE(value)
CU_ASSERT_FALSE_FATAL(value)
Assert that value is FALSE (zero)
CU_ASSERT_EQUAL(actual, expected)
CU_ASSERT_EQUAL_FATAL(actual, expected)
Assert that actual = = expected
CU_ASSERT_NOT_EQUAL(actual, expected))
CU_ASSERT_NOT_EQUAL_FATAL(actual, expected)
Assert that actual != expected
CU_ASSERT_PTR_EQUAL(actual, expected)
CU_ASSERT_PTR_EQUAL_FATAL(actual, expected)
Assert that pointers actual = = expected
CU_ASSERT_PTR_NOT_EQUAL(actual, expected)
CU_ASSERT_PTR_NOT_EQUAL_FATAL(actual, expected)
Assert that pointers actual != expected
CU_ASSERT_PTR_NULL(value)
CU_ASSERT_PTR_NULL_FATAL(value)
Assert that pointer value == NULL
CU_ASSERT_PTR_NOT_NULL(value)
CU_ASSERT_PTR_NOT_NULL_FATAL(value)
Assert that pointer value != NULL
CU_ASSERT_STRING_EQUAL(actual, expected)
CU_ASSERT_STRING_EQUAL_FATAL(actual, expected)
Assert that strings actual and expected are equivalent
CU_ASSERT_STRING_NOT_EQUAL(actual, expected)
CU_ASSERT_STRING_NOT_EQUAL_FATAL(actual, expected)
Assert that strings actual and expected differ
CU_ASSERT_NSTRING_EQUAL(actual, expected, count)
CU_ASSERT_NSTRING_EQUAL_FATAL(actual, expected, count)
Assert that 1st count chars of actual and expected are the same
CU_ASSERT_NSTRING_NOT_EQUAL(actual, expected, count)
CU_ASSERT_NSTRING_NOT_EQUAL_FATAL(actual, expected, count)
Assert that 1st count chars of actual and expected differ
CU_ASSERT_DOUBLE_EQUAL(actual, expected, granularity)
CU_ASSERT_DOUBLE_EQUAL_FATAL(actual, expected, granularity)
Assert that (actual - expected) <= (granularity)
Math library must be linked in for this assertion.
CU_ASSERT_DOUBLE_NOT_EQUAL(actual, expected, granularity)
CU_ASSERT_DOUBLE_NOT_EQUAL_FATAL(actual, expected, granularity)
Assert that (actual - expected) > (granularity)
Math library must be linked in for this assertion.
CU_PASS(message) Register a passing assertion with the specified message. No logical test is performed.
CU_FAIL(message)
CU_FAIL_FATAL(message)
Register a failed assertion with the specified message. No logical test is performed.

注意:带有 FATAL 的断言遇到错误时,将中止测试,即后面的测试用例将不会执行;不带 FATAL 的断言遇到错误时,会继续测试后面的测试用例。

这些断言并不会让进程异常,即使带有 FATAL 的断言遇到错误时,也只是不再测试后面的测试用例,但通过CU_add_suite 注册的 CU_CleanupFunc 函数依然会被执行。

4.5 更多参考

  • CUnit官网Users Guide:http://cunit.sourceforge.net/doc/index.html
  • GitHub范例:以关键词 CU_ASSERT 搜索GitHub可以找到很多范例。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章