当前位置: 首页 > news >正文

【可测试性实践】C++ 单元测试代码覆盖率统计入门

引言

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

常用C++单测框架对比

特性Google Test (gtest)Catch2Boost.TestCppUnit
开发者GooglePhil NashBoost社区CppUnit社区
许可证BSD 3-ClauseBoost Software LicenseBoost Software LicenseLGPL
平台支持跨平台跨平台跨平台跨平台
集成易于和CMake集成易于和CMake集成易于和CMake集成需要手动集成
断言风格宏定义 (ASSERT_*)宏定义 (REQUIRE, CHECK)宏定义 (BOOST_*)宏定义 (CPPUNIT_*)
测试发现自动自动自动手动
Mock支持需要第三方库需要第三方库需要第三方库需要第三方库
文档丰富的官方文档丰富的官方文档丰富的官方文档较少
社区支持强大活跃强大较少
扩展性较低
学习曲线平缓平缓较陡较陡
主要特点高性能, 多线程支持简洁, 可读性强功能强大, 但复杂基础功能

详细说明

  1. Google Test (gtest):
    • 优点: 强大的社区支持,丰富的文档,高性能,支持多线程测试。
    • 缺点: Mock功能需要额外的库(如Google Mock)。
  2. Catch2:
    • 优点: 代码简洁,测试代码可读性强,单头文件,集成方便。
    • 缺点: Mock功能需要额外的库。
  3. Boost.Test:
    • 优点: 功能强大,丰富的断言和测试功能,兼容Boost库。
    • 缺点: 学习曲线较陡,文档虽然丰富但略显复杂。
  4. 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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 用go语言实现树和哈希表算法
  • (一)模式识别——基于SVM的道路分割实验(附资源)
  • Qt什么时候触发paintEvent事件
  • Selenium 实现图片验证码识别
  • 封装智能指针 qt实现登录界面
  • 衡石分析平台使用手册-部署前准备
  • 如何快速撰写毕业论文任务书
  • 遍历有向网络链路实现
  • 初学者如何掌握python
  • nlohmann::json中有中文时调用dump转string抛出异常的问题
  • 瑞吉外卖—读写分离
  • 机器学习:opencv图像识别--模版匹配
  • 华为OD机试真题E卷-计算网络信号(含题目描述+解题思路+代码解析)
  • 前端打包装包——设置镜像
  • 机试算法模拟题 服务中心选址
  • 【347天】每日项目总结系列085(2018.01.18)
  • CSS盒模型深入
  • Elasticsearch 参考指南(升级前重新索引)
  • JavaScript 奇技淫巧
  • JavaScript学习总结——原型
  • Java面向对象及其三大特征
  • PHP 7 修改了什么呢 -- 2
  • spring boot下thymeleaf全局静态变量配置
  • TypeScript实现数据结构(一)栈,队列,链表
  • 产品三维模型在线预览
  • 给初学者:JavaScript 中数组操作注意点
  • 给第三方使用接口的 URL 签名实现
  • 基于HAProxy的高性能缓存服务器nuster
  • 盘点那些不知名却常用的 Git 操作
  • 容器服务kubernetes弹性伸缩高级用法
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • 使用SAX解析XML
  • 使用权重正则化较少模型过拟合
  • 通过几道题目学习二叉搜索树
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • 仓管云——企业云erp功能有哪些?
  • 机器人开始自主学习,是人类福祉,还是定时炸弹? ...
  • #控制台大学课堂点名问题_课堂随机点名
  • #每日一题合集#牛客JZ23-JZ33
  • #预处理和函数的对比以及条件编译
  • (1)无线电失控保护(二)
  • (14)Hive调优——合并小文件
  • (4)Elastix图像配准:3D图像
  • (二)WCF的Binding模型
  • (三)uboot源码分析
  • (顺序)容器的好伴侣 --- 容器适配器
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (转)h264中avc和flv数据的解析
  • (自适应手机端)行业协会机构网站模板
  • *p++,*(p++),*++p,(*p)++区别?
  • .equals()到底是什么意思?
  • .NET 中让 Task 支持带超时的异步等待
  • .net后端程序发布到nignx上,通过nginx访问
  • .NET简谈设计模式之(单件模式)
  • ::before和::after 常见的用法