只需一个头文件的超轻量级单元测试框架。因其轻量,故不名 test ,而另名 tast 。
依赖低,只用到 C++ 标准库(甚至无需 C++11 功能)。
- 提供一个统一的宏
COUT
用以检测与预测比较变量或表达式的值。其名来源于std::cout
,初衷即是将信息打印出来看看是啥。 - 提供
DEF_TAST
宏定义一个测试用例函数(对象),然后用RUN_TAST
宏自动执行所有按此定义的测试用例。且可在命令行参数指定执行特定的测试用例。 - 也可以自由定义无参无返回值的测试函数
void()
并手动安排调用。不过若用TAST
宏调用,可额外打一行标题信息,然后可用TAST_RESULT
打印测试统计信息。
只需在测试文件中包含 tinytast.hpp
头文件,然后用 DEF_TAST
定义一系列类似
void()
函数的代码块,其中可用 COUT
输出或比较感兴趣的值。
// test-file1.cpp
#include "tinytast.hpp"
DEF_TAST(test_name, "测试用例说明")
{
COUT(sizeof(int));
COUT(sizeof(int), 4);
COUT(sizeof(int)==4, true);
}
在 main()
只要调用 RUN_TAST
宏即可。
// main.cpp
#include "tinytast.hpp"
int main(int argc, char** argv)
{
return RUN_TAST(argc, argv);
}
将相关测试 *.cpp
源文件编译链接在一起即可运行。
另注:v0.1 简易版不支持
DEF_TAST
定义用例及 RUN_TAST
运行用例。只支持 TAST
调用普通 void()
函
数。
make
make example
在本仓库主目录上提供了 makefile
,直接 make
会生成 lib/
与 bin/
子目录。
但这不是必须的,用户可以直接只使用 include
的头文件。
有三种方法构建可执行测试程序(命令行程序),可参见 example/basiccpp-*
的几个子目录。
- 自己写
main
函数。可以在调用RUN_TAST
之前作些特殊的预处理。 - 链接静态库
lib/libtast_main.a
,自己不用写main
函数,只关注写测试用例。libtast_main.a
的功能相当简单,就只为提供个main
入口函数。 - 自己将测试用例编译为动态链接库,要求导出
tast_main()
函数,该函数一般会最 终调用RUN_TAST
宏。然后用bin/tast_drive
程序驱动编译的测试动态库, 将动态库作为第一个命令行参数,剩余参数会传给内部的测试库。
bin/tast_drive libmytast.so --list
bin/tast_drive libmytast.so [test_name_fileter]
注意加载动态库可能要导出环境变量 $LD_LIBRARY_PATH
,毕竟一般不会将测试动态库
放到系统路径中。
本测试库所支持的命令行参数见后文。
顾名思义,COUT
就是将一个表达的值打印出来。支持任意类型,只要其支持 <<
操
作符。
- 单参数
COUT(expr)
:打印expr
表达式字面量及其值,使其输出信息更有可读意义,且更易找到源文件互为参照。 - 双参数
COUT(expr, expect)
:在单参数的基础上扩展,比较expr
与expect
的值,如果相等,在行尾打上 '[OK]' 的标记,否则打上[NO]
的标记。比较失败后 也会再打印expect
的值。表达式与预期值的类型可以不同,但要求支持==
操作重载。 - 浮点数三参数
COUT(expr, expect, limit)
:因为浮点精度原因,直接用==
比较 浮点数易出问题,所以允许提供额外参数指定精度。
但是也应尽量避免浮点数比较,果真需要,可显式如下调用:
COUT(abs(expr-expect) <= limit, true);
单参数的 COUT
也可称为输出宏,双参数(以上)则称为断言宏。
对于断言宏还有两个扩展变种,可选使用。
COUTF(expr, expect)
: 只有当断言失败时才输出信息,用于减少成功时的输出信息。COUT_ASSERT(expr, expect)
: 断言失败时会因return
终止当前测试函数,可避 免因一个关键失败导致后续连续失败甚至异常。
另外提供两个纯输出函数,仅为改善输出信息的可读性,可选使用。
DESC(msg, ...)
:类似printf
调用。CODE(statement)
:执行语句前打印出该语句。
TAST(test_fun)
:调用测试函数名(void()
类型的函数指针)TAST_RESULT
:打印测试统计信息
为不失灵活性,完全可以按常规编程习惯定义一些函数,只要这些函数用到 COUT
宏就
能输出与比较。然后手动在 main()
中组织调用这些函数。不过若是 void()
类型的
函数,则可用 TAST()
宏来调用,将会额外跟踪加入统计信息 TAST_RESULT
。
DEF_TAST(case_name, desc)
:相当于测试函数void tast_case()
加强版,还可 以为该测试用例附加一段描叙说明。本质上是定义了一个类及其实例。随后的{}
代 码块是该类的一个虚函数实现。RUN_TAST()
:自动执行由DEF_TAST
定义的所有测试用例,且按测试名的字典序依 次执行。可以传入(argc, argv)
命令行参数,以筛选执行特定的测试用例。
在定义测试用例时,参数 case_name
要求是合法的 C++ 符号(不加双引号),desc
是
任意描叙字符串(加引号)。描叙参数也可省略,但建议加上便于人工管理测试用例。
在运行测试用例时,RUN_TAST()
最后会自动调用 TAST_RESULT
宏,返回的是失败用
例个数,可直接当作 main
的返回值, 0
表示测试成功。
当多个输入的命令行参数传给 RUN_TAST()
时,除特定选项外,将依次对其解析:
- 如果参数完全匹配某个测试名,则只运行该用例。
- 如果参数仅是单字符,则只运行以该字符开头的用例。
- 如果参数以
^
开头或以星号*
结尾,则查找特定前缀的用例。 - 如果参数以
$
结尾或以星号*
开头,则查找特定后缀的用例。 - 其他无特殊字符时,查找任意包含给定参数的用例。
这些规则应该符合直觉与方便使用。暂不打算支持完整正则表达式,以保证库的轻量化。
已知 bug (特性),多个命令参数筛选的测试用例没有互斥与唯一性。即在非用例全名 的情况下,有可能多次查找到同一个用例,重复运行。
作为选项的命令行参数,都以一个或多个 -
开头,如果选项有参数,只能用 =
分隔
选项名及其参数,即形如 --key
或 --key=val
格式。不支持用空格分隔(否则会当
作普通的测试用例参数)。约定统一的选项参数格式也是为实现轻量化考虑。
目前支持的选项有以下几个:
--help
: 输出简要帮助信息,可用选项。--list
:列出所有测试用例名,而不实际运行测试。--List
:列出所有测试用例名及其描叙,即DEF_TAST
宏定义时的两个参数。--cout=fail
:只输出COUT
断言失败的语句,相当于COUTF
效果。--cout=silent
:与fail
类似,但输出的信息还更精简,单参数COUT
输出宏 与纯描叙性的DESC
宏也被沉默,不再有输出。--cout
只能取这两个值有意义, 其他值都等效于all
表示默认输出全部信息。
另注,--list
与 --help
一样是短路的,输出信息后直接退出,因此也会忽略后面
提供的测试名筛选参数,而是列出所有测试名。如需查询特定测试名,可管道至 grep
或其他更过滤工具。
./tast_program --list | grep something
./tast_program > tast.cout
# after some time for fix ...
mv tast.cout tast.bout
./tast_program > tast.cout
diff tast.cout tast.bout
可将不同时段的测试输出保存文件,用 diff 比较法模拟回归测试。不过此法要注
意的是测试中不宜输出指针地址,因为不同运行时内存地址不可能做到保持一致。另外,
如果测试代码中只用到双参数 COUT
测试,一般看统计结果全部通过即可。diff 比较
法更适用于测试代码中大量使用单参数 COUT
不能自动判断通过与否的情况。