CUnit Guideline

1. Introduction

1.1 Description

        CUnit是一个用C语言编写、管理和运行单元测试的系统。CUnit以静态库的形式提供给用户使用,用户编写程序的时候直接链接此静态库就可以了。CUnit使用一个简单的框架来构建测试结构,并为测试常见数据类型提供了一组丰富的断言。此外,还提供了几个不同的接口用于运行测试和报告结果。其中包括用于代码控制测试和报告的自动化界面,以及允许用户动态运行测试和查看结果的交互式界面。

        数据类型和函数在以下头文件中声明:

Header File

Description

#include <CUnit/CUnit.h>

ASSERT macros for use in test cases, and includes other framework headers.

#include <CUnit/CUError.h>

Error handing functions and data types. Included automatically by CUnit.h.

#include <CUnit/TestDB.h>

Data type definitions and manipulation functions for the test registry, suites, and tests. Included automatically by CUnit.h.

#include <CUnit/TestRun.h>

Data type definitions and functions for running tests and retrieving results. Included automatically by CUnit.h.

#include <CUnit/Automated.h>

Automated interface with xml output.

#include <CUnit/Basic.h>

Basic interface with non-interactive output to stdout.

#include <CUnit/Console.h>

Interactive console interface.

#include <CUnit/CUCurses.h>

Interactive console interface (*nix).

#include <CUnit/Win.h>

Windows interface (not yet implemented).

1.2 Structure

        CUnit是一个独立于平台的框架与各种用户界面的组合。

        用户界面便于与框架交互以运行测试和查看结果。CUnit提供了4种用户接口,每个接口有多个API可供使用。我们使用的是Basic接口,即默认输出到命令行,使用的API是CU_basic_set_mode()。使用Basic接口需要包含此头文件:#include "CUnit/Basic.h"

Interface

Platform

Description

Automated

all

non-interactive with output to xml files

Basic

all

non-interactive with optional output to stdout

Console

all

interactive console mode under user control

Curses

Linux/Unix

interactive curses mode under user control

        CUnit的核心框架为管理测试注册表,套件和测试用例提供了基本支持。CUnit的框架组织方式类似于传统的单元测试框架:

        CUnit的测试是单线程启动,只能注册一个注册表(Test Registry), 一次测试可以运行多个套件(Test Suite),而每个套件可以包括多个测试用例(Test Case),每个测试用例又包含一个或者多个断言类的语句。具体到代码结构上,一个单元测试工程下包含多个Test Suite,它对应于程序中各个独立模块;一个Suite管理多个Test Case,它对应于模块内部函数实现。每个Suite可以含有setup和teardown函数,分别在执行suite的前后调用。

1.3 General Usage

使用CUnit框架的典型步骤顺序是:

  1. 为测试编写函数(必要时还可以使用init/cleanup套件)
  2. 初始化测试注册表:CU_Initialize_registry()
  3. 将套件添加到测试注册表:CU_Add_suite()
  4. 将测试添加到套件:CU_Add_test()
  5. 使用适当的接口运行测试,例如CU_basic_set_mode()
  6. 清理测试注册表:CU_Cleanup_registry()

2. Writing CUnit Test Cases

2.1 Stub

        桩函数(Stub)实际上是白盒测试中的概念,它模拟被测试模块所调用的模块。如果被测试的单元模块需要调用其他模块中的功能或者函数(method),就应设计一个和被调用模块名称相同的桩模块来模拟被调用模块。这个桩模块本身不执行任何功能,仅在被调用时返回静态值来模拟被调用模块的行为。

        为什么要使用桩函数?

  • 加速开发。被替换的函数可能是目前还没写完的,这样能够加速开发,或更好的找错误源
  • 减少测试的模块依赖。在测试模块A中发现问题,但不能确定是否因为调用的函数B引起时,我们使用桩函数来替代函数B,桩函数的功能与函数B的功能完全一致,必须得保证桩函数100%正确,这样我们就能排除是函数B的问题还是模块A的问题。
  • 被测函数注入难以触发的输入。为了达到特定的目的替代原始函数,比如强制改变测试分支,将复杂业务简单化,而不是真实的去构造设置很多业务环境来达到条件,为了节省开支使用简单实用的办法直接替代。
//******************************************************************************
// @brief  : 进行一个桩函数替换
// @param  : void *Func    : 待替换的目标函数
// @param  : void *StubFunc: 桩函数
// @return : 
// @note   : 
//******************************************************************************
int SimStub(void *Func, void *StubFunc);

//******************************************************************************
// @brief  : 恢复被替换的桩函数
// @param  : void *Func    : 被替换的目标函数
// @return : 
// @note   : 
//******************************************************************************
int SimStubReset(void *Func);

        建议在test suite的开始建一个用于Init的test,里面可以调用SimStub完成替换,在suite结束建一个用于Teardown的test,里面调用SimStubReset恢复替换,这样做的好处是可以在suite层面完成替换和恢复替换,避免影响到其他suite。

2.2 Assertions

        断言(Assert)用来判断值是否符合预期,从而判断是否发生了错误;CUnit提供了一组用于测试逻辑条件的断言,使用断言需要包含头文件:#include <CUnit/CUnit.h>。这些断言的成功或失败由框架跟踪,并且可以在测试运行完成时查看。

        每个断言测试一个逻辑条件,如果该条件的计算结果为FALSE,则失败。失败后,除非用户选择断言"xxx_FATAL",否则测试将继续。以下是常用的断言:

断言

功能

CU_ASSERT_TRUE(value)

断言值为True

CU_ASSERT_FALSE(value)

断言值为False

CU_ASSERT_EQUAL(actual, expected)

断言两值相等,注意这里比较的是int类型!

CU_ASSERT_NOT_EQUAL(actual, expected)

断言两值不等,注意这里比较的是int类型!

CU_ASSERT_DOUBLE_EQUAL(actual, expected)

断言两double值相等

CU_ASSERT_DOUBLE_NOT_EQUAL(actual, expected)

断言两double值不等

CU_ASSERT_PTR_EQUAL(actual, expected)

断言两指针值相等

CU_ASSERT_PTR_NOT_EQUAL(actual, expected)

断言两指针值不等

CU_ASSERT_PTR_NULL(value)

断言指针为NULL

CU_ASSERT_PTR_NOT_NULL(value)

断言指针不为NULL

CU_ASSERT_STRING_EQUAL(actual, expected)

断言两字符串相同

CU_ASSERT_STRING_NOT_EQUAL(actual, expected)

断言两字符串不同

CU_PASS(message)

断言进入该分支是正确的

CU_FAIL(message)

断言进入该分支是不正确的

以下是两类特殊的断言

  • xxx_FATAL

        这种断言的含义是:出现这种值错误的后果非常严重,测试没有必要继续进行下去当FATAL断言失败的时候,会发生中断并立即返回,所以写在测试函数里面进行清理的代码段无法执行到,但是正常的套件清理功能不会受到影响。

  • CU_PASS和CU_FAIL

        CU_PASS和CU_FAIL不进行逻辑测试,仅用于测试是否运行到了正确的分支。

void test_longjmp(void)
{
  jmp_buf buf;
  int i;
  i = setjmp(buf);
  if (i == 0) 
  {
      run_other_func();
      CU_PASS("run_other_func() succeeded.");
  }
  else
  {
      CU_FAIL("run_other_func() issued longjmp.");
  } 
}

3. Managing Tests & Suites

        用例和套件不能单独存在,用例(Case)需要注册到套件(Suite)中,套件需要注册到注册表(Test Registry)中。CUnit提供了两种注册方法:常规注册法(通过函数来注册)和快捷注册法(通过结构来注册)。下面介绍下快捷注册。它的主要好处是集中注册套件和相关测试,并最大限度地减少用户需要编写的错误检查代码。

(1)初始化一个CU_TestInfo类型(在<CUnit/TestDB.h>中定义)的数组,构成了套件。

每一行都表示一个在该套件中注册的用例:{用例名, 用例函数}。数组必须以包含NULL值的元素结尾,宏CU_TEST_INFO_NULL方便地定义了NULL值。

(2)初始化一个CU_SuiteInfo类型(在<CUnit/TestDB.h>中定义)的数组,构成了套件的集合。

每一行都表示一个套件:{套件名, 套件初始化函数, 套件清理函数, 用例的Setup函数, 用例的TearDown函数, 套件包含的用例数组}。通常,如果给定套件不需要NULL,则可以将其用于初始化或清理函数。数组必须以全NULL元素结尾,可以使用宏CU_SUITE_INFO_NNULL。

(3)调用CU_register_suites(suites)这个函数就可以完成所有用例和套件的注册。

typedef struct CU_TestInfo;
typedef struct CU_SuiteInfo;  

//******************************************************************************
// @brief  : 快捷注册,完成所有套件和用例的注册
// @param  : CU_SuiteInfo suite_info[] : 套件集合
// @return : 
// @note   : 
//******************************************************************************
CU_ErrorCode CU_register_suites(CU_SuiteInfo suite_info[]);

4. Running Tests

        选中BEP_UT,右键点击"设为启动项目" -> 工具栏选择"X86" -> 工具栏"调试" -> "开始调试" or "开始执行"。

5. Coverage Check Tool

单元测试覆盖率可使用OpenCppCoverage插件。

5.1 安装

        打开VisualStudio-->扩展-->管理扩展-->联机-->右上角输入OpenCppCoverage,在下面的结果会出现"OpenCppCoverage Plugin",点击安装即可,安装完成后需要重启VisualStudio。

        如果下载过程出现进度条卡住几个小时不动 或 弹框“OpenCppCoverage Plugin:在web client请求期间发生异常”等,是VS2019服务器的问题。下载📎OpenCppCoverage-0.9.7.1.7z到本地,解压后安装即可正常使用(在工具栏或者选中工程点击右键可以看到该插件),如果Install过程下方出现End Task和Cancel两个选择框,点击End Task即可。

5.2 使用

        程序写好编译执行之后,点击"工具-->Run OpenCppCoverage",程序运行,代码会出现红色或者绿色的阴影(绿色是覆盖的代码,红色是未覆盖的代码)如下图:

也可以运行OpenCppCoverage Settiings,设置.html报告生成的路径,如下图:

更多内容详见:CUnit - Table of Contents

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值