我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C/C++ 单元自动化测试解决方案实践

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C/C++ 单元自动化测试解决方案实践

C/C++ 开发效率一直被业内开发人员诟病,单元测试开发效率也是如此,以至于开发人员不愿花时间来写单元测试。那么我们是不是可以通过改善编写单元测试的效率来提升项目的测试用例覆盖率?

本文主要介绍如何利用GCC插件来实现提升C/C++开发者的单元效率工具解决方案,希望对大家在提升单元测试效率上有所启发。

一、动机

上图展示了C/C++单元测试的基本流程,在日常开发过程中写单元测试是一项比较大工程量的事情,C/C++ 目前单元测试代码都需要自己手动写,而且对于一些私有方法打桩就更加麻烦。

目前业内无开源的自动化测试框架或者工具,倒是有一些商业的自动测试工具,下图展示了我们自动化测试工具及单元测试库:

即使开源界有gtest等测试库的支持,我们仍然需要编写大量的单元测试用例代码。对于一些private、protected的类方法,编写单元测试用例的效率就更低,需要手动打桩(mock)。同时我们分析测试用例发现,存在很多边界的用例,它们基本上都是很固定或者有一定模式,比如int 最大最小值等。

如何改善编写单元测试的效率,提升C/C++同学开发效率以及程序质量?我们可以通过提取源文件中的函数、类等信息,然后生成对应的单元测试用例。自动生成用例时需要依赖函数的声明、类的声明等信息,那么我们应该如何获取这些信息呢?

例如:如下的函数定义:

void test(int arg) {}

我们希望能够从上面的函数定义中得到函数的返回值类型、函数名称、函数参数类型、函数作用域。通常我们可以通过以下几种方式得到:

1.1 方法1:使用正则表达式

无奈C/C++ 格式比较复杂能够虽然能够使用多种组合来获取对应的函数声明等信息:

 void test(int arg){}
void test1(template<template<string>> arg,...){}
void test2(int(*func)(int ,float,...),template<template<string>> arg2){}

那么就需要写一系列的正则表达式:

  • 提取函数名称、参数名:[z-aA-Z_][0-9]+
  • 提取函数返回值:^[a-zA-Z_]

关键词提取出来了,但是他有一个很大的问题:怎么判断文件中书写的代码是符合C/C++语法描述呢?

1.2 方法2:使用flex/bison 分析c/c++源码文件

这当然是一种很好的方式,但是工作量巨大,相当于实现一个具备词法、语法分析器简易版本的编译器,而且要适配不同的语法格式,虽然bison可以解决上述的如何判断语法是否正确问题,但是仍然很复杂。

1.3 方法3:利用编译已经生成的AST 来生成代码

通常我们了解到的GCC编译的过程是以下四个阶段:

源文件->预处理->编译->汇编→链接

但实际上GCC为了支持更多的编程语言、不同的CPU架构做了很多的优化,如下图所示:

上图展示了GCC处理源码及其他优化过程,在前端部分生成的Generic 语言是gcc编译过程中为源码生成的一种与源码语言无关的抽象语法表现形式(AST)。既然GCC编译过程中生成了AST树,那么我们可以通过GCC插件来提取GCC 前端生成的抽象语法树关键信息比如函数返回值、函数名称、参数类型等。总体难度也很高,一方面业内可参考资料很少,只能通过分析GCC的源码来分析AST语法树上的各个节点描述。

本文所描述的自动化生成单元测试用例的解决方案(我们称之为TU:Translate Unit,后文统称为TU)就是基于方法3来实现的,下面我们先来看看我们的自动化测试用例解决方案的效果展示。

二、效果展示

2.1 业务代码零修改, 直接使用TU生成边界用例

在该用例中我们不需要修改任何业务代码就能够为业务代码生成边界测试用例,而且函数参数可边界值实现全排列,大大降低用例遗漏风险。大家可能发现这种没有做任何修改生成的用例是没有断言的,虽然没有断言,它仍然能够帮助发现单元是否会存在边界值引起coredump。

那么如果想要给他加上断言、mock函数,是否没有办法呢?通过C++11 [[]] 新的属性语法,只需要在方法声明或者定义时添加下根据TU的格式添加断言即可,对业务逻辑无侵入。

2.2 使用注解tu::case生成用户自定义用例

很多情况下默认生成的边界测试用例还不能覆盖到核心逻辑,所以我们也提供tu::case 来给用户自定义自己的测试用例及断言。比如有一个int foo (int x,long y) 方法,现在想新增一个测试用例返回值123,函数实参1,1000,那么只要在函数声明前加入,以下代码即可:

[[tu::case("NE","123","1","1000")]]

2.3 使用注解tu::mock 自动生成mock方法

开发过程中我们也常需要对某个方法进行mock(即对原有方法设置一个临时代替方法并且调用方式保持一致),比如某个函数访问Redis、DB这种情况下进行单元测试往往需要对这些方法进行mock,方便其他函数调用进行单元测试,为了方便进行单元测试我们往往会对其进行mock,所以为了方便开发人员进行快速的mock,所以我们提供了tu::mock 的注解帮助开发同学快速的定义注解,然后TU会自动生成对应的mock函数。例如:现在给foo_read 方法mock一个函数,让mock的函数返回10:

三、TU实现方案

3.1 AST 是什么?

GENERIC、GIMPLE和RTL三者构成了gcc中间语言的全部,它们以GIMPLE为核心,由GENERIC承上,由RTL启下,在源文件和目标指令之间的鸿沟之上构建了一个三层的过渡。

GCC在语法分析过程中,所有识别出来的语言部件都用一个叫TREE的变量保存着。这个TREE就是GCC语法树(AST),这个过程叫做GENERIC。实际上它也是GCC的符号表,因为变量名、类型等等这些信息都由TREE关联起来。

下面我们通过gcc编译选项来看下gcc的ast表现形式:

3.2 AST(Abstract syntax tree)

GCC 可以通过添加编译选项-fdump-tree-all 来生成ast 树,ast树文件内容如下:

AST 各个类型描述可以参考:https://gcc.gnu.org/onlinedocs/gccint/Types.html

虽然上图中简单看下一下可以发现,gcc这种表现形式节点与节点之间还存在依赖,比较难于理解,没有clang生成的直观更容易阅读。虽然不利于阅读,但是不影响通过编码来提取AST信息。

3.3 方案

如上图所示,我们通过使用不同的插件收集被测试源文件的AST信息、头文件信息、函数注解(属性),将这些重要信息保存起来。GCC将用户注册插件事件保存到数组中:

然后在编译构建过程中到就会去查找对应的事件有没有设置回调方法如果设置则进行调用,TU主要使用以下几种插件:

  • PLUGIN_INCLUDE_FILE 用于获取当前文件的所包含的头文件
  • PLUGIN_OVERRIDE_GATE 用户获取普通函数、类
  • PLUGIN_PRE_GENERICIZE 用于获取模板函数的具现化
  • PLUGIN_ATTRIBUTES 用于实现自定义属性或者注解(tu::case\tu::mock ....)

GCC 支持的所有插件类型如下图所示:(摘自gcc 6.3.0 源码)

四、TU 插件使用的简易程度对比

如果仅仅只是做边界测试那么仅需要修改构建的脚本比如cmake 添加对应的插件参数即可。

五、使用TU的优点

  • 接入简单、边界单元测试可以做到业务代码0修改
  • 函数参数可边界值实现全排列,大大降低用例遗漏风险、减少大量重复性的工作
  • 快速生成用户自定义用例、mock方法等

六、TU支持的功能

七、总结与展望

1、文章中对比了三种方法自动生成测试用例的方法,下面对这几种方法进行对比:

2、文章中还主要介绍了TU的功能特点以及基于GCC-AST的实现自动生成测试用例的解决方案。

TU解决方案目前在构建时能够自动生成测试用例已经极大降低了单元测试门槛提升单元测试覆盖率,未来我们也希望能够把TU与IDE相结合,探索更高效便捷的使用方式,通过更加便捷的方式生成指定方法的测试用例。比如通过在函数、方法上,通过快捷键生成当前方法的测试用例等。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C/C++ 单元自动化测试解决方案实践

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C/C++ 单元自动化测试解决方案实践

本文主要介绍如何利用GCC插件来实现提升C/C++开发者的单元效率工具解决方案,希望对大家在提升单元测试效率上有所启发。

C++ 函数单元测试的自动化策略?

c++++ 函数单元测试的自动化策略:使用框架:google test、catch2 等框架简化了测试编写和执行。隔离代码:保证函数独立于其他代码部分,便于测试。mock 和 stub:模拟函数或依赖项的行为,以进行单元测试。脚本或 ci
C++ 函数单元测试的自动化策略?
2024-04-23

Android自动化测试解决方案

现在,已经有大量的Android自动化测试架构或工具可供我们使用,其中包括:Activity Instrumentation, MonkeyRunner, Robotium, 以及Robolectric。另外LessPainful也提供服务
2022-06-06

C#开发建议:测试驱动开发与单元测试实践

C#(C Sharp)是一种通用、面向对象的编程语言,它在软件开发领域得到了广泛的应用。对于C#开发者来说,掌握一些开发建议是非常有益的,可以提高代码的质量和开发效率。本文将介绍测试驱动开发(TDD)和单元测试在C#开发中的实践,并分享一些
C#开发建议:测试驱动开发与单元测试实践
2023-11-22

C#中如何使用单元测试框架进行自动化测试

C#中如何使用单元测试框架进行自动化测试引言:在软件开发过程中,自动化测试是一个非常重要的环节。通过编写和运行测试代码,可以帮助我们验证和确保代码的正确性和稳定性。在C#开发中,我们可以使用单元测试框架来实现自动化测试。本文将介绍C#中常用
2023-10-22

PHP 单元测试实践中的常见问题与解决方案

php 单元测试的常见问题:外部依赖项测试: 使用模拟框架(如 mockery)创建假的依赖项并断言其交互。私有成员测试: 使用反射 api(如 reflectionmethod)访问私有成员或使用测试可见性修饰符(如 @protected
PHP 单元测试实践中的常见问题与解决方案
2024-05-06

函数单元测试的最佳实践和自动化框架

在函数单元测试中,最佳实践包括:隔离测试、清晰定义输入和预期结果、使用断言、遵循 dry 原则、考虑边界条件和模拟依赖关系。自动化框架可以简化和加速测试,其中 mocha 和 jest 是两个流行的选择。mocha 灵活易用,提供各种断言库
函数单元测试的最佳实践和自动化框架
2024-04-12

C++嵌入式系统开发的自动化测试实践

自动化测试实践可提高嵌入式系统开发的质量、效率和可重复性。对于 c++++ 开发,google test 框架提供了丰富的断言、用例生成器和 ide 集成。实战案例演示了使用 google test 验证预期结果,为测试用例编写 expec
C++嵌入式系统开发的自动化测试实践
2024-05-12

PHP 单元测试:实践中遇到的常见问题及解决方案

php 单元测试陷阱:依赖项注入困难:使用依赖项注入容器(例如 prophecy 或 mockery)和接口/抽象类。模拟数据库交互困难:使用依赖抽象层(例如 doctrine),模拟查询,或在虚拟环境中运行数据库。测试覆盖率低:使用覆盖率
PHP 单元测试:实践中遇到的常见问题及解决方案
2024-05-21

Go语言性能测试的自动化解决方案

go语言自动化性能测试解决方案:使用vegeta和goconvey框架。该解决方案包括以下步骤:使用vegeta创建攻击或负载测试。使用goconvey进行bdd测试,验证服务器响应是否为200 ok。使用vegeta的histogram以
Go语言性能测试的自动化解决方案
2024-05-07

详解渗透测试和漏洞扫描的开源自动化解决方案

在本文中,我们将探讨漏洞扫描和渗透测试领域,重点介绍每个步骤中所涉及的各个阶段之间的微妙差异和相似之处。最后,我们还将详细介绍几个专注于任务自动化的工具。为了让大家更容易理解,我们将对渗透测试和漏洞评估的基本步骤进行分解。

火山引擎 DataLeap 计算治理自动化解决方案实践和思考

本文旨在探讨火山引擎 DataLeap 在处理计算治理过程中所面临的问题及其解决方案,并展示这些解决方案带来的实际收益。

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录