【可测试性实践】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
/src
add.cpp
add.h
main.cpp
/test
test_add.cpp
CMakeLists.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_H
int 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