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

引言

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

常用C++单测框架对比

特性 Google Test (gtest) Catch2 Boost.Test CppUnit
开发者 Google Phil Nash Boost社区 CppUnit社区
许可证 BSD 3-Clause Boost Software License Boost Software License LGPL
平台支持 跨平台 跨平台 跨平台 跨平台
集成 易于和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)
bash 复制代码
sudo apt-get update
sudo apt-get install libboost-all-dev
在 Windows 上

你可以从 Boost 官方网站下载并安装 Boost 库。

在 Mac 上

可以通过 Homebrew 安装 Boost库:

bash 复制代码
brew install boost

步骤二:创建项目结构

示例工程结构:

plain 复制代码
/boost.test
    /src
        add.cpp
        add.h
        main.cpp
    /test
        test_add.cpp
    CMakeLists.txt

步骤三:编写 CMakeLists.txt

在项目根目录下创建或编辑 CMakeLists.txt 文件:

cmake 复制代码
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中添加以下代码:

cpp 复制代码
#ifndef ADD_H
#define ADD_H

int add(int a, int b);

#endif // ADD_H

src/add.cpp中添加以下代码:

cpp 复制代码
#include "add.h"

int add(int a, int b) {
    return a + b;
}

src/main.cpp 中添加以下代码:

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 中添加以下代码:

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);
}

步骤六:构建和运行测试

在项目根目录下打开终端或命令提示符,并执行以下命令:

bash 复制代码
# 创建构建目录
mkdir build
cd build

# 生成构建文件并编译项目
cmake ..
make

# 运行测试
./test_main

你应该会看到类似于以下的输出,表示测试通过:

plain 复制代码
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报告。

生成覆盖率报告

bash 复制代码
# 生成初始的覆盖率信息
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环境跑通了。

附录

相关推荐
长弓聊编程9 分钟前
Linux系统使用valgrind分析C++程序内存资源使用情况
linux·c++
cherub.16 分钟前
深入解析信号量:定义与环形队列生产消费模型剖析
linux·c++
暮色_年华30 分钟前
Modern Effective C++item 9:优先考虑别名声明而非typedef
c++
重生之我是数学王子38 分钟前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
我们的五年1 小时前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习
做人不要太理性1 小时前
【C++】深入哈希表核心:从改造到封装,解锁 unordered_set 与 unordered_map 的终极奥义!
c++·哈希算法·散列表·unordered_map·unordered_set
程序员-King.2 小时前
2、桥接模式
c++·桥接模式
chnming19872 小时前
STL关联式容器之map
开发语言·c++
程序伍六七2 小时前
day16
开发语言·c++
小陈phd2 小时前
Vscode LinuxC++环境配置
linux·c++·vscode