6 C++与Jenkins和SonarQube
GoogleTest 是 Google 开发的一个跨平台的 C++ 测试框架,用于编写和运行单元测试。它是目前 C++ 生态中最流行的测试框架之一,广泛应用于工业界和开源项目中。
✔丰富的断言机制- 提供多种断言宏用于测试验证 ✔测试发现机制- 自动注册测试用例,无需手动维护测试列表 ✔测试固件(Test Fixtures)- 支持测试环境的设置和清理 ✔参数化测试- 支持使用不同参数运行同一测试逻辑 ✔死亡测试- 专门测试程序崩溃或断言失败的情况 ✔与构建系统集成- 支持CMake、Bazel等主流构建系统
断言类型 | 示例 | 说明 |
---|---|---|
基本断言 | ASSERT_TRUE(condition) | 条件为真时通过 |
二进制比较 | EXPECT_EQ(val1, val2) | 期望相等(推荐使用) |
字符串比较 | ASSERT_STREQ(str1, str2) | C字符串比较 |
浮点比较 | EXPECT_DOUBLE_EQ(a, b) | 浮点数近似相等 |
异常检查 | EXPECT_THROW(statement, exc_type) | 检查是否抛出特定异常 |
cpp
#include <gtest/gtest.h>
TEST(TestSuiteName, TestName) {
EXPECT_EQ(2+2, 4);
ASSERT_NE(5, 3) << "额外的失败信息";
}
2 测试固件(Fixture)
cpp
class MyFixture : public ::testing::Test {
protected:
void SetUp() override {
// 测试前初始化
data = new int(42);
}
void TearDown() override {
// 测试后清理
delete data;
}
int* data;
};
TEST_F(MyFixture, Test1) {
ASSERT_NE(data, nullptr);
EXPECT_EQ(*data, 42);
}
3 参数化测试
bash
# 使用vcpkg
vcpkg install gtest
# 使用apt (Ubuntu)
sudo apt install libgtest-dev
# 使用源码编译
git clone https://github.com/google/googletest.git
cd googletest
mkdir build && cd build
cmake .. && make && sudo make install
6.1.3. 安装与集成
bash
# 使用vcpkg
vcpkg install gtest
# 使用apt (Ubuntu)
sudo apt install libgtest-dev
# 使用源码编译
git clone https://github.com/google/googletest.git
cd googletest
mkdir build && cd build
cmake .. && make && sudo make install
2 CMake集成示例
cmake
find_package(GTest REQUIRED)
add_executable(MyTests test1.cpp test2.cpp)
target_link_libraries(MyTests GTest::GTest GTest::Main)
6.1.4. 高级特性
1 死亡测试
cpp
TEST(DeathTest, InvalidPointer) {
int* p = nullptr;
EXPECT_DEATH(*p = 42, ".*Segmentation fault.*");
}
2 类型参数化测试
cpp
template <typename T>
class TypedTest : public ::testing::Test {};
TYPED_TEST_SUITE_P(TypedTest);
TYPED_TEST_P(TypedTest, SizeTest) {
EXPECT_GT(sizeof(TypeParam), 0);
}
REGISTER_TYPED_TEST_SUITE_P(TypedTest, SizeTest);
using MyTypes = ::testing::Types<char, int, double>;
INSTANTIATE_TYPED_TEST_SUITE_P(MyPrefix, TypedTest, MyTypes);
3 测试事件监听
cpp
class MyListener : public ::testing::EmptyTestEventListener {
void OnTestStart(const ::testing::TestInfo& test_info) override {
std::cout << "Starting test: " << test_info.name() << std::endl;
}
};
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::UnitTest::GetInstance()->listeners().Append(new MyListener);
return RUN_ALL_TESTS();
}
6.1.5. 最佳实践
1.测试命名规范:使用TestSuiteName_TestName_Test风格
2.优先使用EXPECT而非ASSERT,除非后续测试依赖前置条件
3.保持测试独立:测试之间不应有依赖关系
4.测试代码质量:测试代码应与产品代码保持相同质量标准
5.结合覆盖率工具:如gcov/lcov确保测试充分性
工具 | 优点 | 缺点 |
---|---|---|
GoogleTest | 功能全面、社区活跃、文档完善 | 需要额外学习成本 |
Catch2 | 单头文件、简单易用 | 功能相对有限 |
Boost.Test | 与Boost生态集成 | 编译时间长 |
Doctest | 极简设计、编译快 | 功能较少 |
✅ 单元测试驱动开发(TDD) ✅ 回归测试保障 ✅ API接口测试 ✅ 算法正确性验证 ✅ 跨平台兼容性测试
stages{
stage('gtest'){
steps {
echo "开始编译gtest"
sh '''
cd /home/jerry/googletest-main/googletest/myworkspace/process/nomal/ \
&&g++ ../../src/TestAll.cpp process.h process.cpp processTest.cpp ../../src/CallArgs.h ../../src/CallArgs.cpp -o process -lgtest -lgmock -lpthread -std=c++14 -fprofile-arcs -ftest-coverage \
&&./process --gtest_output="xml:output.xml"\
&&xsltproc ./gtest2html/gtest2html.xslt ./output.xml >./out/out.html
'''
echo "结束编译gtest"
}
}
…
post{
always{
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'/home/jerry/googletest-main/googletest/myworkspace/process/nomal/out/',
reportFiles:'out.html',
reportName:'My Test Reports',
reportTitles:'The Test Report'])
}
6.2 Gcov介绍及在Jenkins中的应用
Gcov(GNU Coverage)是GCC(GNU Compiler Collection)自带的一个代码覆盖率分析工具,用于统计程序中哪些代码被执行过,哪些没有被执行。它常用于单元测试、软件测试优化和代码质量分析,帮助开发者发现未测试的代码路径。
·语句覆盖率(Statement Coverage):统计每行代码是否被执行。
·分支覆盖率(Branch Coverage):检查if-else、switch-case等分支是否被覆盖。
·函数覆盖率(Function Coverage):统计哪些函数被调用过。
·输出详细的覆盖率报告(.gcov 文件),便于分析。
使用-fprofile-arcs -ftest-coverage编译选项(GCC):
bash
gcc -fprofile-arcs -ftest-coverage -o my_program my_program.c
·-fprofile-arcs:记录代码执行路径。
·-ftest-coverage:生成 .gcno 文件(用于后续分析)。
bash
./my_program
运行后,会生成.gcda文件(记录实际执行情况)。
bash
gcov my_program.c
·生成my_program.c.gcov文件,显示每行代码的执行次数。
·示例输出:
text
-: 10: printf("Hello, World!\n");
1: 11: return 0;
o-表示未执行,1表示执行了1次。
text
-: 0:Source:my_program.c
-: 0:Graph:my_program.gcno
-: 0:Data:my_program.gcda
-: 0:Runs:1
-: 0:Programs:1
1: 1:#include <stdio.h>
1: 2:
1: 3:int main() {
1: 4: printf("Hello, Gcov!\n");
1: 5: return 0;
-: 6:}
·1:表示该行代码执行了 1 次。
·-:表示未执行(如注释、空行等)。
bash
lcov -c -d . -o coverage.info
# 收集覆盖率数据
genhtml coverage.info -o coverage_report
# 生成 HTML 报告
·打开coverage_report/index.html查看可视化报告:
https://www.linux.com/images/stories/41373/gcov-html.png
1.单元测试覆盖率检查
o确保测试用例覆盖了所有关键代码路径。
2.回归测试优化
o发现哪些代码在修改后未被测试。
3.代码质量分析
o识别冗余或无效代码(从未执行的部分)。
·免费且开源,集成在 GCC 中,无需额外安装。
·轻量级,适合小型到中型项目。
·支持 C/C++(GCC 编译的语言)。
·仅适用于 GCC,不适用于 Clang/MSVC。
·报告较原始,需结合 lcov/gcovr 生成更好看的报告。
·不能检测运行时错误(如内存泄漏),仅统计代码执行情况。
工具 | 特点 |
---|---|
LCOV | 基于 Gcov,生成 HTML 报告,可视化更强 |
gcovr | Python 编写的 Gcov 报告生成工具,支持多种输出格式 |
LLVM-cov | Clang/LLVM 的覆盖率工具,类似 Gcov |
BullseyeCoverage | 商业工具,支持更多高级功能 |
·Gcov是GCC自带的代码覆盖率工具,适用于 C/C++ 项目。
·基本使用流程:
1.用-fprofile-arcs -ftest-coverage编译代码。
2.运行程序,生成.gcda文件。
3.用gcov生成报告,或lcov生成 HTML 可视化报告。
·适用场景:单元测试、回归测试、代码优化。
如果你需要更强大的可视化,建议结合LCOV或gcovr使用!
stage('gcov'){
steps {
echo "开始gcov"
sh '''
gcov -a process-process\
&&gcovr -r . --html-details -o ./html/result.html
'''
echo "结束gcov"
}
}
…
post{
always{
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'/home/jerry/googletest-main/googletest/myworkspace/process/nomal/html/',
reportFiles:'result.html',
reportName:'My Test gCover Reports',
reportTitles:'The Test gCover Report'])
}
6.3 Lcov介绍及在Jenkins中的应用
Lcov是一个基于Gcov的代码覆盖率分析工具,用于生成更直观、可视化的覆盖率报告(如 HTML)。它扩展了 Gcov 的功能,提供更友好的图形化界面,适用于C/C++项目的测试覆盖率分析。
·支持多种覆盖率统计:
o行覆盖率(Line Coverage):代码行是否被执行。
o函数覆盖率(Function Coverage):函数是否被调用。
o分支覆盖率(Branch Coverage):条件分支(如 if-else)是否被覆盖。
·生成HTML报告,便于可视化分析。
·支持过滤数据(如排除某些目录或文件)。
·适用于大型项目,比Gcov更易于管理。
bash
gcc -fprofile-arcs -ftest-coverage -o my_program my_program.c
• -fprofile-arcs 和 -ftest-coverage 用于生成覆盖率数据(.gcno 文件)。
bash
./my_program
运行后,会生成.gcda文件(记录实际执行情况)。
bash
lcov -c -d . -o coverage.info
·-c:捕获覆盖率数据。
·-d .:从当前目录查找.gcda文件。
·-o coverage.info:输出到coverage.info文件。
bash
genhtml coverage.info -o coverage_report
·-o coverage_report:生成 HTML 报告到 coverage_report 目录。
·打开coverage_report/index.html查看报告:
https://ftp.tutorials24x7.com/uploads/1/1/1/0/111016327/lcov-report_orig.png
·目录/文件列表:显示每个文件的覆盖率。
·颜色标识:
o绿色:已覆盖的代码。
o红色:未覆盖的代码。
o黄色:部分覆盖(如分支未完全覆盖)。
·详细数据:
o每行代码的执行次数。
o函数和分支的覆盖率统计。
text
Overall coverage rate:
lines......:85.3% (100 of 117 lines)
functions..:92.1% (35 of 38 functions)
branches...:76.5% (52 of 68 branches)
6.3.4. Lcov 的高级用法
bash
lcov --remove coverage.info "/usr/include/*" "*/test/*" -o filtered.info
·--remove用于排除不需要分析的文件(如系统头文件或测试代码)。
bash
lcov -a coverage1.info -a coverage2.info -o combined.info
·-a用于合并多个测试运行的覆盖率数据。
bash
lcov --json -o coverage.json
·可用于集成到 CI/CD 工具(如 Jenkins、GitLab CI)。
·可视化强:HTML 报告比 Gcov 的文本报告更直观。
·支持多种覆盖率统计(行、函数、分支)。
·适用于大型项目,可过滤无关文件。
·开源免费,与 Gcov 无缝集成。
·依赖 Gcov,仅适用于 GCC 编译的代码(不适用于 Clang/MSVC)。
·HTML 报告较大,可能不适合超大型项目。
·需要额外安装(部分 Linux 发行版默认不包含)。
工具 | 特点 |
---|---|
gcovr | Python 实现,支持 XML/HTML 报告,比 Lcov 更轻量 |
LLVM-cov | Clang/LLVM 的覆盖率工具,类似 Lcov |
BullseyeCoverage | 商业工具,支持更多高级功能 |
Coverage.py | Python 项目的覆盖率工具 |
·Lcov 是 Gcov 的增强版,提供 HTML 可视化报告。
·典型使用流程:
1.用-fprofile-arcs -ftest-coverage编译代码。
2.运行程序,生成.gcda文件。
3.用lcov收集数据,genhtml生成报告。
·适用场景:
oC/C++ 项目的单元测试覆盖率分析。
o持续集成(CI)中的代码质量检查。
推荐使用场景:
·如果你在用GCC + Gcov,并需要更好的可视化,Lcov 是最佳选择!
·如果项目较小,可以试试gcovr(更轻量)。
stage('lcov'){
steps {
echo "开始lcov"
sh '''
lcov -c -d . -o coverage.info\
&&genhtml coverage.info -o coverage_report
'''
echo "结束lcov"
}
}
…
post{
always{
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'/home/jerry/googletest-main/googletest/myworkspace/process/nomal/coverage_report/',
reportFiles:'index.html',
reportName:'My Test lCover Reports',
reportTitles:'The Test lCover Report'])
}
6.4 CPPcheck介绍及在Jenkins中的应用
Cppcheck是一个开源的静态代码分析工具,专门用于检测C/C++ 代码中的错误。它不依赖编译器,而是直接分析源代码,能发现许多编译器无法检测的问题(如内存泄漏、逻辑错误等)。
✔轻量级:比 Clang Static Analyzer 等工具更快 ✔低误报率:相比部分静态分析工具更准确 ✔跨平台:支持 Windows/Linux/macOS ✔可扩展:支持自定义规则 ✔支持多种标准:C89/C99/C11/C++03/C++11/C++14/C++17
检测类型 | 典型问题示例 |
---|---|
内存管理 | 内存泄漏、双重释放、空指针解引用 |
资源泄漏 | 文件描述符未关闭、数据库连接未释放 |
逻辑错误 | 死代码、无限循环、条件永远为真/假 |
API使用错误 | memset大小错误、printf格式不匹配 |
多线程问题 | 竞态条件、死锁风险 |
代码风格问题 | 未使用的变量、冗余代码 |
安全性问题 | 缓冲区溢出、格式化字符串漏洞 |
bash
# Linux (Debian/Ubuntu)
sudo apt install cppcheck
# macOS
brew install cppcheck
# Windows
choco install cppcheck
# 或从官网下载二进制包
2基本扫描命令
bash
# 检查单个文件
cppcheck example.cpp
# 递归检查整个项目
cppcheck --enable=all ./src/
# 输出到文件
cppcheck --output-file=report.txt main.cpp
3常用选项
选项 | 说明 |
---|---|
--enable= | 启用额外检查(all/warning/style/performance...) |
-I | 添加头文件搜索路径 |
-j | 使用N个线程并行分析 |
--platform= | 指定平台(unix32/unix64/win32/win64) |
--std= | 指定语言标准(c++11/c++14等) |
bash
cppcheck --xml-version=2 --xml . 2> report.xml
2与CMake集成
cmake
find_program(CPPCHECK cppcheck)
if(CPPCHECK)
add_custom_target(cppcheck
COMMAND ${CPPCHECK}
--enable=all
--project=${CMAKE_BINARY_DIR}/compile_commands.json
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
endif()
3自定义规则
创建misra.json:
json
{
"script": "misra.py",
"args": ["--rule-texts=misra.txt"]
}
运行:
bash
cppcheck --addon=misra.json src/
6.4.5. 典型输出示例
text
Checking example.cpp...
[example.cpp:12]: (error) Memory leak: ptr
[example.cpp:25]: (warning) Possible null pointer dereference: p
[example.cpp:42]: (style) Variable 'i' is not used
6.4.6. 同类工具对比
工具 | 优势 | 劣势 |
---|---|---|
Cppcheck | 低误报、速度快、资源占用低 | 对模板代码支持有限 |
Clang-Tidy | 深度分析、支持现代C++ | 高资源消耗、误报较多 |
PVS-Studio | 商业级精度、支持跨文件分析 | 收费、配置复杂 |
Coverity | 企业级解决方案 | 价格昂贵、需要服务器 |
1.集成到CI/CD:在代码提交/合并时自动运行
2.渐进式启用:先启用基本检查,逐步增加规则
3.排除第三方代码:--suppress=*:external/*
4.定期更新:新版会持续改进检测能力
5.结合动态分析:与Valgrind/ASAN等工具互补使用
Cppcheck 是C/C++开发者工具箱中不可或缺的静态分析工具,特别适合:
·需要快速反馈的中小型项目
·资源受限的嵌入式开发环境
·作为编译警告之外的额外质量保障
推荐使用场景: ✅ 每日开发中的快速检查 ✅ CI流水线中的基础质量门禁 ✅ 遗留代码的质量评估
stage('cppcheck'){
steps {
echo "开始cppcheck"
sh '''
cppcheck --enable=all --output-file=report.html process.cpp
'''
echo "结束cppcheck"
}
}
…
post{
always{
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'/home/jerry/googletest-main/googletest/myworkspace/process/nomal/',
reportFiles:'report.html',
reportName:'Cppcheck Reports',
reportTitles:'Cppcheck Report'])
}
6.5 一个例子
pipeline {
agent any
stages{
stage('gtest'){
steps {
echo "开始编译gtest"
sh '''
cd /home/jerry/googletest-main/googletest/myworkspace/process/nomal/ \
&&g++ ../../src/TestAll.cpp process.h process.cpp processTest.cpp ../../src/CallArgs.h ../../src/CallArgs.cpp -o process -lgtest -lgmock -lpthread -std=c++14 -fprofile-arcs -ftest-coverage \
&&./process --gtest_output="xml:output.xml"\
&&xsltproc ./gtest2html/gtest2html.xslt ./output.xml >./out/out.html
'''
echo "结束编译gtest"
}
}
stage('gcov'){
steps {
echo "开始gcov"
sh '''
gcov -a process-process\
&&gcovr -r . --html-details -o ./html/result.html
'''
echo "结束gcov"
}
}
stage('lcov'){
steps {
echo "开始lcov"
sh '''
lcov -c -d . -o coverage.info\
&&genhtml coverage.info -o coverage_report
'''
echo "结束lcov"
}
}
stage('cppcheck'){
steps {
echo "开始cppcheck"
sh '''
cppcheck --enable=all --output-file=report.html process.cpp
'''
echo "结束cppcheck"
}
}
stage('Code Analysis'){
steps {
echo "开始分析"
withSonarQubeEnv('SonarQube'){
sh "cd /home/jerry/googletest-main/googletest/myworkspace/process/nomal/ &&sonar-scanner"
}
echo "结束分析"
}
}
stage('Quality Gate'){
steps{
echo "开始质量分析"
script {
timeout(time:10,unit:'MINUTES'){
sleep(5)
try{
def qg = waitForQualityGate()
echo "Status: ${qg.status}"
if (qg.status != 'OK') {
echo "Status: ${qg.status}"
error "Pipeline aborted due to quality gate failure: ${qg.status}"
}
}
catch(exc){
echo "waitForQualityGate() is error"
echo "${exc}"
}
}
}
echo "结束质量分析"
}
}
}
post{
always{
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'/home/jerry/googletest-main/googletest/myworkspace/process/nomal/out/',
reportFiles:'out.html',
reportName:'My Test Reports',
reportTitles:'The Test Report'])
}
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'/home/jerry/googletest-main/googletest/myworkspace/process/nomal/',
reportFiles:'report.html',
reportName:'Cppcheck Reports',
reportTitles:'Cppcheck Report'])
}
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'/home/jerry/googletest-main/googletest/myworkspace/process/nomal/html/',
reportFiles:'result.html',
reportName:'My Test gCover Reports',
reportTitles:'The Test gCover Report'])
}
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'/home/jerry/googletest-main/googletest/myworkspace/process/nomal/coverage_report/',
reportFiles:'index.html',
reportName:'My Test lCover Reports',
reportTitles:'The Test lCover Report'])
}
}
}
}
6.5.2 总报告