- 单元测试的定义
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,C语言中的单元指一个函数。 - 为什么单元测试
尽早发现错误,错误发现越早,成本越低。开发人员过于自信,后期复杂度高,发现解决BUG困难.检查代码是否符合设计和规范。 - 单元测试的目标
- 单元模块被正确编码。
- 信息能正确地流入和流出单元。
- 在单元工作过程中,其内部数据能保持其完整性,包括内部数据的形式、内容及相互关系不发生错误,也包括全局变量在单元中的处理和影响。
- 在为限制数据加工而设置的边界处,能正确工作。
- 单元的运行能做到满足特定的逻辑覆盖。
- 单元中发生了错误,其中的出错处理措施有效。
- 单元测试框架介绍及环境搭建
当前的单元测试,使用的是Ceedling框架。
GitHub地址:
https://github.com/ThrowTheSwitch/Ceedling
4.1 Ceedling介绍
Ceedling提供了一个简单的接口,用于配置从基础到复杂的项目结构。从而可以使用ceedling进行简单工程、有多个依赖库依赖项的工程、多个依赖工具工具的工程的测试代码构建;
Ceedling是基于unity的框架,是针对C语言的核心测试框架,专用于嵌入式的;
Ceedling是对Ruby的Rake (make-ish)构建系统的扩展,Ceedling主要针对C语言中的测试驱动开发(Test-Driven Development),旨在整合CMock(生成模拟接口)、unity(单元测试框架)和 CException(异常处理框架) 这三个很棒的开源项目。
4.2 环境搭建
4.2.1 安装Ruby
安装命令:
sudo apt install ruby
验证是否安装成功:
ruby –v
4.2.2 安装autoConfig
安装命令:
sudo apt-get install autoconf
验证是否安装成功:
autoconf –v
4.2.3 安装ceeding
安装命令:
sudo gem install ceedling --version=0.30.0
验证是否安装成功:
ceedling version
4.2.4 安装gcover
源码位置:https://github.com/gcovr/gcovr
安装命令:
pip install gcovr
验证是否安装成功:
gcovr --version
gcov版本必须为4.X以上
执行如下命令,查看gcovr的目录
pip show gcovr
4.3 Unity介绍
Unity是一个单元测试框架。我们的目标是保持它的规模和功能。核心的Unity测试框架有三个文件:一个C文件和两个头文件。它们共同提供函数和宏,使测试更容易。
GitHub地址:
https://github.com/ThrowTheSwitch/Unity
4.3.1 Unity常用断言
断言参数的约定通常遵循以下顺序:
TEST_ASSERT_X( {modifiers}, {expected}, actual, {size/count} )
“Actual”是被测试的值,与断言构造中的其他参数不同,它是所有断言变体中惟一的参数。“Modifiers”包括掩码、范围、位标志说明符、浮点增量。“期望值”是你的期望值(duh)与“实际”值的比较;它被标记为一个可选参数,因为一些断言只需要一个“实际”参数(例如null check)。“Size/count”指的是字符串长度、数组元素数量等。
TEST_ASSERT_X_MESSAGE( {modifiers}, {expected}, actual, {size/count}, message )
_MESSAGE变量故意不支持printf样式的格式化,因为许多嵌入式项目由于各种原因不支持或避免printf。如果需要,可以在断言之前使用sprintf来组装复杂的失败消息。
如果您希望在断言失败消息(例如,从循环中)中输出计数器值,那么构建一个结果数组,然后使用_ARRAY断言之一(参见下面)可能是sprintf的一个方便的替代方法。
TEST_ASSERT_EQUAL_TYPEX_ARRAY( expected, actual, {size/count} )
“Expected”本身就是一个数组。“Size/count”是一个或两个必要的参数,用于确定数组元素的数量和数组中元素的长度。
_MESSAGE变量约定仍然适用于数组断言。_ARRAY断言的_MESSAGE变量的名称以_ARRAY_MESSAGE结尾。
TEST_ASSERT_EACH_EQUAL_TYPEX( expected, actual, {size/count} )
“Expected”是一个可以比较的单一值。“实际”是一个数组,其中每个元素都将与期望值进行比较。“Size/count”是确定数组元素的数量以及数组中元素的长度所必需的两个参数之一。
_MESSAGE变量约定仍然适用于每个相等的断言
在这里插入图片描述
4.4 CMoke介绍
CMock是一个很好的小工具,它可以获取你的头文件并为它创建一个模拟接口,这样你就可以更容易地对模块进行单元测试。
GirHub地址:
https://github.com/ThrowTheSwitch/CMock
4.4.1 CMoke基本用法
对于头文件中的每个函数原型,如
int DoesSomething(int a, int b);
你得到一个自动生成的DoesSomething函数,你可以链接到它,而不是你真正的DoesSomething函数。通过使用这个模拟版本,您可以验证它是否接收到您想要的数据,并让它返回您想要的任何数据,让它在您想要的时候抛出错误,等等……为您最新的实际模块接触的所有东西创建这些,您就会突然处于一个强大的位置:您可以控制和验证您最新创建的每个细节。
为了使这更容易,CMock也给你一堆像下面的函数,所以你可以告诉生成的DoesSomething函数如何为每个测试行为:
void DoesSomething_ExpectAndReturn(int a, int b, int toReturn);
void DoesSomething_ExpectAndThrow(int a, int b, EXCEPTION_T error);
void DoesSomething_StubWithCallback(CMOCK_DoesSomething_CALLBACK YourCallback);
void DoesSomething_IgnoreAndReturn(int toReturn);
你可以把这些东西背靠背堆起来,它就会记住你想传递的内容,比如:
test_CallsDoesSomething_ShouldDoJustThat(void)
{
DoesSomething_ExpectAndReturn(1,2,3);
DoesSomething_ExpectAndReturn(4,5,6);
DoesSomething_ExpectAndThrow(7,8, STATUS_ERROR_OOPS);
CallsDoesSomething( );
}
这个测试将调用CallsDoesSomething,这是我们正在测试的函数。我们期望这个函数调用三次做某事。第一次,我们检查以确保它被称为DoesSomething(1,2),并神奇地返回一个3。第二次我们检查DoesSomething(4,5)并返回一个6。第三次我们验证做了一些事情(7,8),我们将抛出一个错误而不是返回任何东西。如果calls做的事情有任何错误,它将失败测试。如果你没有调用足够的,或调用太多,或使用错误的参数,或以错误的顺序调用,它将会失败。
CMock基于Unity,用于所有内部测试。它使用Ruby完成所有主要工作(版本2.0.0及以上)。