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

2024-09-12 19:28:53 浏览数 (1)

引言

最近在调研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)
代码语言:javascript复制
sudo apt-get update
sudo apt-get install libboost-all-dev
在 Windows 上

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

在 Mac 上

可以通过 Homebrew 安装 Boost库:

代码语言:javascript复制
brew install boost

步骤二:创建项目结构

示例工程结构:

代码语言:javascript复制
/boost.test
    /src
        add.cpp
        add.h
        main.cpp
    /test
        test_add.cpp
    CMakeLists.txt

步骤三:编写 CMakeLists.txt

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

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

代码语言:javascript复制
#ifndef ADD_H
#define ADD_H

int add(int a, int b);

#endif // ADD_H

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

代码语言:javascript复制
#include "add.h"

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

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

代码语言:javascript复制
#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 中添加以下代码:

代码语言:javascript复制
#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);
}

步骤六:构建和运行测试

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

代码语言:javascript复制
# 创建构建目录
mkdir build
cd build

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

# 运行测试
./test_main

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

代码语言:javascript复制
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-arcsftest-coverage 编译选项,以启用代码覆盖率信息的生成。
  • 测试代码:
    • #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报告。

生成覆盖率报告

代码语言:javascript复制
# 生成初始的覆盖率信息
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

0 人点赞