c++自动化测试框架,c语言自动化测试框架
Yyds干货库存
Vivo互联网服务器团队-李庆新
C/C开发的效率一直被业内开发者所诟病,单元测试开发的效率也是如此,以至于开发者都不愿意花时间写单元测试。那么我们能否通过提高编写单元测试的效率来提高项目的测试用例覆盖率呢?本文主要介绍如何使用GCC插件实现提高C/C开发人员单元效率的工具解决方案,希望对大家提高单元测试效率有所启发。
一.动机
上图显示了C/C单元测试的基本流程。日常开发过程中编写单元测试是一个比较大的工作量。目前C/C单元测试代码需要手工编写,一些私有方法堆起来比较麻烦。目前业界还没有开源的自动化测试框架或工具,但是有一些商业化的自动化测试工具。下图显示了我们的自动化测试工具和单元测试库:
即使开源社区有gtest等测试库的支持,我们仍然需要编写大量的单元测试用例代码。对于一些私有和受保护的类方法,编写单元测试用例的效率更低,需要手工打桩(mock)。同时我们对测试用例进行分析,发现有很多边界用例,基本上都是非常固定的,或者有一定的模式,比如int的最大值和最小值。如何提高编写单元测试的效率,提高C/C学生的开发效率和程序质量?我们可以从源文件中提取函数、类等信息,然后生成相应的单元测试用例。用例的自动生成需要依赖函数和类的声明等信息,那么应该如何获取这些信息呢?例如,下面的函数定义:
void test(int arg) {}
我们希望从上面的函数定义中得到返回值类型,函数名,函数参数类型,函数作用域。我们通常可以通过以下方式获得:
1.1方法一:使用正则表达式是无奈之举。C/C格式很复杂。虽然您可以使用各种组合来获得相应的函数声明和其他信息:
void test(int arg){}
void test1(模板模板字符串arg,){}
Voidtest2 (int (* func) (int,float,),模板模板字符串arg2) {}然后你需要写一系列正则表达式:
提取函数名,参数名:\[z-aA-Z_\]\[0-9\]提取函数返回值:\ [A-ZA-Z _ \]关键词提取出来了,但是他有一个很大的疑问:如何判断文件中写的代码是否符合C/C语法描述?
1.2方法二:用flex/bison分析c/c源文件当然是一个好办法,但是工作量巨大,相当于用一个简单版本的词法和语法分析器实现一个编译器,而且要适应不同的语法格式。虽然bison可以解决上述如何判断语法是否正确的问题,但是还是很复杂。
1.3方法三:通过编译已经生成的AST来生成代码。通常,我们所知道的GCC编制过程是以下四个阶段:
源文件-预处理-编译-汇编链接
但实际上GCC已经做了很多优化来支持更多的编程语言和不同的CPU架构,如下图所示:
上图显示了gcc对源代码的处理和其他优化过程。前面部分生成的泛型语言是GCC编译时为源代码生成的抽象语法表示(AST),与源代码语言无关。由于AST树是在GCC编译时生成的,所以我们可以通过GCC插件提取GCC前端生成的抽象语法树的关键信息,如函数返回值、函数名、参数类型等。整体难度也很高。一方面,业内参考资料较少,AST语法树中各个节点的描述只能通过分析GCC的源代码来分析。本文描述的自动生成单元测试用例的解决方案(我们称之为TU: Translate Unit,以下统称为TU)是基于方法3实现的。让我们首先来看看我们的自动化测试用例解决方案的效果展示。
二。效果演示2.1零修改业务代码,直接使用TU生成边界用例。
在这个用例中,我们可以在不修改任何业务代码的情况下,为业务代码生成边界测试用例,功能参数可以完全用边界值排列,大大降低了遗漏用例的风险。你可能会发现,这种没有任何修改就生成的用例是没有断言的。虽然没有断言,但仍然可以帮助找出单元格中是否会有边界值会导致coredump。那么如果你想给他添加断言和mock函数,是不是没有办法?通过C 11 \[\[\]\] new属性语法,只需要在声明或定义方法时按照TU格式添加断言,对业务逻辑没有入侵。
2.2使用注释tu:case生成用户定义的用例。很多情况下,默认生成的边界测试用例无法覆盖核心逻辑,所以我们也提供了tu:case供用户自定义自己的测试用例及断言。举个例子,如果有一个int foo (int x,long y)方法,现在想添加一个返回值为123,函数参数为11000的测试用例,那么只需要在函数声明之前添加,下面的代码就可以了:
\[\[tu:case(NE , 123 , 1 , 1000)\]\]
2.3使用注释tu:mock自动生成mock方法。在开发过程中,我们经常需要对某个方法进行mock(即给原方法设置一个临时的替代方法,并保持调用方法的一致性)。例如,当一个函数访问Redis和DB时,经常需要模仿这些方法,以便于其他函数调用的单元测试。为了方便单元测试,我们经常会对其进行模拟,所以为了方便开发者快速模拟,我们提供了tu:mock comments来帮助开发者快速定义注释,然后tu会自动生成相应的模拟函数。比如:现在给foo_read方法一个函数进行mock,让mock的函数返回10:
三。什么是TU实现方案3.1 AST?GENERIC、GIMPLE和RTL构成了整个gcc中间语言。他们以GIMPLE为核心,GIMPLE由GENERIC继承,由RTL开创,在源文件和目标指令之间建立了一个三层过渡。GCC在解析过程中,所有被识别的语言成分都用一个叫做TREE的变量保存。这个树就是GCC语法树(AST),这个过程叫做泛型。其实也是GCC的符号表,因为变量名,类型等等都是用树关联的。我们通过gcc编译选项来看看gcc的ast表达式:
3.2 ast(抽象语法树)GCC可以通过添加编译选项-fdump-tree-all来生成ast树。AST树文件的内容如下:
每种AST类型的描述可参考:https://gcc.gnu.org/onlinedocs/gccint/Types.html
虽然简单看一下上图可以看出gcc中节点之间存在依赖关系,没有clang产生的直觉,很难理解,更容易阅读。虽然不利于阅读,但不影响编码提取AST信息。
3.3方案
如上图所示,我们使用不同的插件收集被测源文件的AST信息、头文件信息和函数注释(属性),并保存这些重要信息。GCC将用户注册插件事件保存到一个数组中:
然后在编译构建的过程中,我们会发现对应的事件是否设置了回调方法。如果是这样,我们就称之为。TU主要使用以下插件:
插件\_INCLUDE\_FILE用于获取当前文件的头文件插件\_OVERRIDE\_GATE。用户获取普通函数,使用类PLUGIN\_PRE\_GENERICIZE获取模板函数的实例化PLUGIN_ATTRIBUTES。它用于实现自定义属性或注释(tu:case\\tu:mock.)
GCC支持的所有插件类型如下图所示:(来自gcc 6.3.0源代码)
四。TU插件的易用性比较
如果只做边界测试,只需要修改构建好的脚本,比如cmake,添加相应的插件参数即可。
5.使用TU的优点:访问方便,边界单元测试可以使业务代码0修改函数参数,实现边界值的全排列,大大降低了遗漏用例的风险,减少了大量重复性工作,快速生成用户自定义用例、mock方法等。6.TU支持的功能
七。总结与展望1。本文比较了三种自动生成测试用例的方法。比较了以下方法:
2.文章还重点介绍了TU的功能特点和基于GCC-AST的测试用例自动生成的解决方案。
目前TU方案在构建时就可以自动生成测试用例,大大降低了单元测试的门槛,提高了单元测试的覆盖率。未来,我们也希望将TU与IDE相结合,探索更高效、更便捷的使用方法,以更便捷的方式生成指定方法的测试用例。比如通过函数和方法,快捷键生成当前方法的测试用例。
参考资料:
【1】 gcc插件
【2】C语言的函数(GNU编译器集合(GCC)内部)
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。