【可测试性实践】C++ 单元测试代码覆盖率统计入门
引言
最近在调研C++工程怎么做单元测试和代码覆盖率统计,由于我们工程有使用Boost库,尝试使用Boost.Test来实现单元测试并通过Gcov和Lcov来生成代码覆盖率报告。本文记录完整的搭建测试Demo,希望能带来一定参考。
常用C++单测框架对比
特性 | Google Test (gtest) | Catch2 | Boost.Test | CppUnit |
---|---|---|---|---|
开发者 | Phil Nash | Boost社区 | CppUnit社区 | |
许可证 | BSD 3-Clause | Boost Software License | Boost Software License | LGPL |
平台支持 | 跨平台 | 跨平台 | 跨平台 | 跨平台 |
集成 | 易于和CMake集成 | 易于和CMake集成 | 易于和CMake集成 | 需要手动集成 |
断言风格 | 宏定义 (ASSERT_*) | 宏定义 (REQUIRE, CHECK) | 宏定义 (BOOST_*) | 宏定义 (CPPUNIT_*) |
测试发现 | 自动 | 自动 | 自动 | 手动 |
Mock支持 | 需要第三方库 | 需要第三方库 | 需要第三方库 | 需要第三方库 |
文档 | 丰富的官方文档 | 丰富的官方文档 | 丰富的官方文档 | 较少 |
社区支持 | 强大 | 活跃 | 强大 | 较少 |
扩展性 | 高 | 高 | 高 | 较低 |
学习曲线 | 平缓 | 平缓 | 较陡 | 较陡 |
主要特点 | 高性能, 多线程支持 | 简洁, 可读性强 | 功能强大, 但复杂 | 基础功能 |
详细说明
- Google Test (gtest):
- 优点: 强大的社区支持,丰富的文档,高性能,支持多线程测试。
- 缺点: Mock功能需要额外的库(如Google Mock)。
- Catch2:
- 优点: 代码简洁,测试代码可读性强,单头文件,集成方便。
- 缺点: Mock功能需要额外的库。
- Boost.Test:
- 优点: 功能强大,丰富的断言和测试功能,兼容Boost库。
- 缺点: 学习曲线较陡,文档虽然丰富但略显复杂。
- CppUnit:
- 优点: 基础功能稳定,适合老项目。
- 缺点: 社区支持较少,文档不丰富,集成和扩展性较差。
使用Boost.Test框架实现单元测试
假设你工程使用是Boost库,可以通过Boost.Test来实现单元测试。
步骤一:安装 Boost 库
如果你还没有安装 Boost 库,可以按照以下步骤进行安装:
在 Linux 上(例如 Ubuntu)
sudo apt-get update
sudo apt-get install libboost-all-dev
在 Windows 上
你可以从 Boost 官方网站下载并安装 Boost 库。
在 Mac 上
可以通过 Homebrew 安装 Boost库:
brew install boost
步骤二:创建项目结构
示例工程结构:
/boost.test/srcadd.cppadd.hmain.cpp/testtest_add.cppCMakeLists.txt
步骤三:编写 CMakeLists.txt
在项目根目录下创建或编辑 CMakeLists.txt
文件:
cmake_minimum_required(VERSION 3.10)
project(boost.test)# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)# 查找 Boost 库
find_package(Boost REQUIRED COMPONENTS unit_test_framework)if(Boost_FOUND)include_directories(${Boost_INCLUDE_DIRS})link_directories(${Boost_LIBRARY_DIRS})
else()message(FATAL_ERROR "Could not find Boost")
endif()# 添加编译选项以支持代码覆盖率
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID)set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage -lgcov")message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")message(STATUS "CMAKE_EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}")
endif()# 包含 src 目录,以便找到 add.h
include_directories(${CMAKE_SOURCE_DIR}/src)# 添加源文件
add_executable(boost.test src/main.cpp src/add.cpp)# 添加测试可执行文件
add_executable(test_main test/test_add.cpp src/add.cpp)
target_link_libraries(test_main Boost::unit_test_framework)
步骤四:编写源文件
在 src/add.h
中添加以下代码:
#ifndef ADD_H
#define ADD_Hint add(int a, int b);#endif // ADD_H
在src/add.cpp
中添加以下代码:
#include "add.h"int add(int a, int b) {return a + b;
}
在 src/main.cpp
中添加以下代码:
#include <iostream>int add(int a, int b) {return a + b;
}int main() {std::cout << "2 + 3 = " << add(2, 3) << std::endl;return 0;
}
步骤五:编写测试文件
在 test/test_add.cpp
中添加以下代码:
#define BOOST_TEST_MODULE test_main
#include <boost/test/included/unit_test.hpp>
#include "add.h"BOOST_AUTO_TEST_CASE(test_add) {BOOST_CHECK(add(2, 3) == 5);BOOST_CHECK(add(0, 0) == 0);BOOST_CHECK(add(-1, -1) == -2);
}
步骤六:构建和运行测试
在项目根目录下打开终端或命令提示符,并执行以下命令:
# 创建构建目录
mkdir build
cd build# 生成构建文件并编译项目
cmake ..
make# 运行测试
./test_main
你应该会看到类似于以下的输出,表示测试通过:
Running 1 test case...
*** No errors detected
详细说明
- CMakeLists.txt:
find_package(Boost REQUIRED COMPONENTS unit_test_framework)
用来查找 Boost 库。add_executable(test_main test/test_main.cpp src/add.cpp)
用来添加测试可执行文件。target_link_libraries(test_main Boost::unit_test_framework)
用来链接 Boost.Test 库。- 添加
-fprofile-arcs</font>
和-ftest-coverage</font>
编译选项,以启用代码覆盖率信息的生成。
- 测试代码:
#define BOOST_TEST_MODULE MyTest
定义测试模块名称。#include <boost/test/included/unit_test.hpp>
包含 Boost.Test 的头文件。BOOST_AUTO_TEST_CASE(test_add)
定义一个测试用例。
使用gcov + lcov统计代码覆盖率
准备工作
确保已经安装以下工具:
- CMake:用于构建项目。
- GCC:支持代码覆盖率生成(其他编译器如 Clang 也可以,但这里以 GCC 为例)。
- gcov:GCC 自带的代码覆盖率工具。
- lcov:用于生成 HTML 格式的覆盖率报告。
- genhtml:用于将 lcov 生成的覆盖率数据转换为 HTML 文件。
GCOV 代码覆盖率统计流程
由于gcov生成的代码覆盖率统计文件可视化较低,所以需要借助lcov,genhtml工具直接生成html报告。
生成覆盖率报告
# 生成初始的覆盖率信息
lcov --capture --directory . --output-file coverage.info# 过滤掉不需要的文件(如系统库和测试框架)
lcov --remove coverage.info '/usr/*' --output-file coverage.info
lcov --remove coverage.info '*/test/*' --output-file coverage.info# 生成 HTML 报告
genhtml coverage.info --output-directory out
查看覆盖率报告
代码覆盖率总览
add.cpp
代码覆盖率统计
main.cpp
代码覆盖率统计
遇到问题
笔者的开发环境主要是Mac+VSCode,但Lcov对Mac系统并不太友好,前面的demo工程虽然编译通过了,但生成代码覆盖率报告就报错,猜测Mac的符号表机制跟Linux不太一样,最后还是在私有构建机的Linux环境跑通了。
附录
- https://github.com/google/googletest
- https://github.com/catchorg/Catch2
- https://www.boost.org/doc/libs/1_86_0/libs/test/doc/html/index.html
- https://cppunit.github.io/cppunit/
- https://github.com/linux-test-project/lcov
- https://blog.csdn.net/qq_32534441/article/details/90645316
相关文章:

【可测试性实践】C++ 单元测试代码覆盖率统计入门
引言 最近在调研C工程怎么做单元测试和代码覆盖率统计,由于我们工程有使用Boost库,尝试使用Boost.Test来实现单元测试并通过Gcov和Lcov来生成代码覆盖率报告。本文记录完整的搭建测试Demo,希望能带来一定参考。 常用C单测框架对比 特性Goo…...

C++笔记---list
1. list的介绍 list其实就是就是我们所熟知的链表(双向循环带头结点),但其是作为STL中的一个类模板而存在。 也就是说,list是可以用来存储任意类型数据的顺序表,既可以是内置类型,也可以是自定义类型&…...

JavaWeb开发中为什么Controller里面的方法是@RequestMapping?
在Java Web开发中,尤其是在使用Spring MVC框架时,RequestMapping注解被广泛应用于Controller层的方法上,这是因为RequestMapping是Spring MVC提供的一个核心注解,用于将HTTP请求映射到相应的处理器类或处理器方法上。通过这种方式…...

若依移动版使用微信小程序打开失败
现象 解决办法:删掉自带的appid...

精准控图工具 Concept Sliders:超好用的 控制 Lora 适配器
Concept Sliders 你有没有遇到这样的情况?你花费大量时间制作提示和寻找种子,以使用文本到图像模型生成所需的图像。但是,你还需要对生成图像中的属性强度(如眼睛大小或照明)进行更细致、更精细的控制。修改提示会破坏…...

【EI会议征稿通知】第四届材料工程与应用力学国际学术会议(ICMEAAE 2025)
第四届材料工程与应用力学国际学术会议(ICMEAAE 2025) 2025 4th International Conference on Materials Engineering and Applied Mechanics 本次会议将重点讨论材料科学、应用力学等领域的最新研究进展与发展趋势。会议旨在为国内外从事这些领域研究…...
Hadoop安全之Knox
Apache Knox 是一个 REST API 网关,为 Hadoop 集群提供安全的访问方式。Knox 提供了一层保护,简化了对 Hadoop 生态系统(如 HDFS、YARN、Hive、HBase 等)中各个组件的访问,并通过单点登录 (SSO)、认证、授权和审计功能…...

SprinBoot+Vue应急信息管理系统的设计与实现
目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质…...
索尼研究的AI部门将与AI新加坡合作开发大型语言模型
索尼研究公司签署了一项合作协议,以帮助测试和优化东南亚语言一网通(SEA-LION)人工智能(AI)模型,重点关注印度语言。 索尼研究公司的AI部门将与负责开发AI新加坡(AISG)的公司合作&a…...

【OJ刷题】双指针问题
这里是阿川的博客,祝您变得更强 ✨ 个人主页:在线OJ的阿川 💖文章专栏:OJ刷题入门到进阶 🌏代码仓库: 写在开头 现在您看到的是我的结论或想法,但在这背后凝结了大量的思考、经验和讨论 目录 1…...

基于SpringBoot+Vue+MySQL的校园食堂订餐
系统展示 用户前台界面 管理员后台界面 系统背景 随着信息技术的飞速发展和互联网的普及,传统校园食堂的运作模式已难以满足现代学生日益增长的便捷性、个性化需求。学生们希望能够在忙碌的学习生活中,通过更加高效、便捷的方式完成就餐选择,…...

uniapp业务实现
uni.requset添加异常判断提示,以及加载动画 /*** 该函数用于发送网络请求获取数据* 请求失败时会弹出相应的错误提示* 请求成功时会检查返回的数据是否存在错误,并根据错误代码做出相应处理* 如果数据请求成功且无错误,则将返回的数据赋值给pets变量*/fu…...
Windows和Mac命令窗快速打开文件夹
Windows explorer . 和 macOS open . 命令详解 1. Windows explorer . explorer 是 Windows 上的文件资源管理器,用于通过命令行打开文件夹或文件。 常用命令格式: explorer [选项] [目标路径]. 表示当前目录,explorer . 打开当前工作目录…...

智能制造云平台---附源码79117
目 录 摘要 1 绪论 1.1 研究背景和意义 1.2开发技术 1.2.1 Flask框架 1.2.2 Python简介 1.2.3 MySQL数据库 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2总体设计原则 2.3 系统流程分析 2.3.1 用户登录流程 2.3.2 删除信息流程 2.4 系统角色分析 2.5 系…...

降本、创新、合作,谁才是连接器行业破除内卷的关键词?
如果用一个字来评价2024年的汽车行业,那就是「卷」。 ▲中国汽车保有量不断提升 图/Pixabay 长安汽车董事长朱华荣说:“汽车行业的卷,让中国品牌达到了新高度。” 吉利董事长李书福说:“中国汽车工业内卷程度全球第一,…...

可能一拆为二,英特尔为何走到今天这一步?
【科技明说 | 科技热点关注】 近来看到外媒消息说,英特尔迫于经营压力,也不得不铤而走险,欲将英特尔一分为二,即芯片制造与芯片设计分离开,互相剥离,独立发展。 于是乎,英特尔将分拆…...

了解Redis集群概念,集群如何选举主节点
请给胡广一个免费的三连吗?感谢! 1. Redis集群 1.1 集群概念 Redis主从架构和Redis集群架构是两种不同的概念,大家刚接触Redis时经常弄混淆。胡广给大家贴下Redis官网对两者的解释。 (1)Redis主从架构 Redis主从实…...

Ozon跨境商家提升销量的关键:测评补单策略与必备条件
Ozon,自1998年创立以来,已稳居俄罗斯多品类电商领域的领导地位,不仅是俄罗斯最为人所熟知的电商品牌,更是该国电商行业的先驱之一。那么,对于希望在Ozon平台上实现销售爆单的跨境卖家而言,他们需要满足哪些…...

缺乏大模型经验,还有机会吗?
做大模型一年半,经历了无数场面试。 关于经验,我最常听到的候选人(尤其是学生)的说辞是:我没有大模型经验,可以给个机会吗?答案是,我们并不看重候选人的大模型训练经验。这里不是说经验不重要,而是大部分人…...

如何阅读李冬梅老师《数据结构》
根据《如何阅读一本书》第五章:主动阅读的基础:阅读者要提出的4个基本问题? 以第2章,线性表为例: (1)本章主要在谈些什么?例如第二章简介,读完这一章可以自己试着写个简…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...