-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【设计】多个测试用例共享初始化代码机制 #10
Comments
后来又想了一下,专门设计一个新宏 试想一下,用户这么写个 DEF_INIT()
{
...
} 与手动写个一次性初始化函数: static void init_test()
{
CALL_FUNCTION_ONCE();
...
} 工作量差不多的,之前用的 至于后面每个测试用例体需要多一行代码显式调用 此外,如果某次写个简单的用例,用不着共享初始化,也可以临时注释掉。而用 还有一点,如果想实现 所以这个功能似乎非必需,普适性不够。真有需求了,按这里的推荐方法手动写初始化函 |
还是实现了这个功能。宏名与初设有所不同。 提出了 unit 的概念,双关,表示一个编译单元即源文件包含的一组单元测试,可能构成一个测试模块,可能需要有个统一的一次性初始化函数 ,可以用于申请共享资源,比如关联全局变量,当然最好是文件级静态变量。也可以有个可选的清理函数,非必需。
定义用例的宏名类似 在运行任一个 这个实现方式是为了在不给普通 可能存在极端罕见情况,在某些命令行参数筛选之下,所有 在当前文件仍可定义自由的普通的 初始化 另外,测试单元 |
问题与背景
有时在写单元测试用例时,每个或多个用例的开始都要写一些通用的初始代码,比如说建
立数据库连接,或其他较消耗较大的加载初始化。
最原始的想法是,抽出一个公用函数,然后由单元测试用例调用之,如:
这样在命令行单独指定运行每一个用例都正常,但默认无参运行所有用例时,可能会有问题。
即使初始化函数是可重入幂等的,重复执行也是一种浪费。
手动初步改进方案是在设计初始化函数时,采用一些技巧,让它只执行一次,类似单例机
制,例如:
这样,每个测试用例调用它一次,都是无害的,也是有效的,可以保证只初始化一次。
实际上也为这三行代码定义了一个宏
CALL_FUNCTION_ONCE
可供方便使用。自动初始化机制提案
以上手动档的方案,明确调用,其实也足够简单有效。
但如果想要往自动化进步一点,允许用户不用手动调用初始化函数,可以如何设计呢?
初步方案如下:
以文件(编译单元)为模块,每个文件内可以定义一个
DEF_INIT
初始化函数,然后在这个文件内定义的其他单元测试用例,都隐式自动调用这个初始化函数。目前,每个由
DEF_TAST
或DEF_TOOL
定义的用例,都保存了当前文件名(与行号)信息,所以理论上是可以找到当前文件内定义的
DEF_INIT
,可以实现该机制。DEF_INIT
也没必要写在文件开始,其他用例之前,但写在前面也是个好风格与好主意。
略有存疑的是
DEF_INIT
宏内,是否要加参数。如果限定每个文件内唯一,省去命名的麻烦是个优点。不过能命名的话,虽然要手动保证不出现命名冲突,也有不限唯一的灵活
性,以及在必要时手动显式调用,比如需要调用(依赖)另一个文件模块的初始化。
综合考虑,我还是倾向于,如技术实现方便,还是按简单原则,文件级唯一初始化函数不必
命名,但是加个可选的描叙性参数是允许的。真还想手动显式调用或复杂依赖时,可返回
前面手动调用拆解函数及
CALL_FUNCTION_ONCE
插入标记的机制。性能代价交换
保存每个文件可能的
DEF_INIT
函数,典型如用std::map
,在执行每个测试用例时需要额外执行一次按文件名查找。这需要一定代价,但应在可接受范围内。
共享清理工作的必要性考察
想到这个概念来源于 gtest 的 test fixture ,一类自定义测试套件,除了其中每个测
试用例有独立的初始与清理代码,还有个针对整个套件的只调一次的初始化函数与清理函
数。
在 Issue #3 中,也扩展了对测试套件的简单支持,但出于基本理念的不同,只可定义每
个用例的初始化
setup
与清理teardown
,并没有针对套件的一次初始化与清理。如果要对某部分相关测试用例分组,我觉得按这里提案,按文件级分组比用基类分组更直
观。所以在一个文件中若定义了
DEF_INIT
,它既可影响该文件内用DEF_TAST
定义的普通用例,也可影响用
DEC_TAST
定义的套件用例。即使没有实现这个DEF_INIT
,也可以在自定义套件类的初始化
setup
函数中额外调用一个只运行一次的函数(
CALL_FUNCTION_ONCE
的手动保证)然而,并没有能简单实现的针对一组相关用例的一次清理函数机制。例如,在文件级分组,
试图定义一个
DEF_UNINIT
会比DEF_INIT
复杂得多。但我觉得这个有点是伪需求,只在极端情况下用得到。比如两组以上的单元测试用例,每
组内有大量用例,每组有个共享的一次性初始代码,其中申请了大量资源,需要在这组用
例运行完释放资源,避免在运行下一组测试用例时浪费资源。对此,我的建议要么容忍占
用资源,单元测试程序即使耗时,也不是死循环的长期服务,运行完自然会释放。要么对
每组测试用例分开编译,不必硬塞到同一个测试程序中。
在 couttast 库,若一定要实现文件级分组测试用例的一次清理工作,也并非不可能。在
解析完命令行过滤待运行用例后,制定测试计划,找出同文件的用例进行管理,某个文件
内的用例全运行完后,再调用清理代码。只不过,这个功能实现的性价比,比文件级分组
的一次初始化要低得多。
The text was updated successfully, but these errors were encountered: