1 Jenkins和SonarQube介绍
1.1 Jenkins介绍
Jenkins 是一个开源的持续集成(CI)和持续交付(CD)工具,用于自动化构建、测试和部署软件项目。它基于 Java 开发,支持跨平台运行,并拥有丰富的插件生态系统,可以灵活地扩展功能。
1.1.1核心功能
1持续集成(CI)
自动触发代码构建和测试,确保每次代码提交后快速发现错误。
支持与 Git、SVN 等版本控制系统集成。
2持续交付/部署(CD)
自动化部署到测试、预发布或生产环境。
支持 Docker、Kubernetes、云平台(AWS、Azure 等)。
3任务调度
定时执行任务(如夜间构建)。
支持手动触发或通过 Webhook(如 GitHub Push 事件)触发。
4丰富的插件生态
超过1,500个插件,支持代码质量分析(SonarQube)、通知(Slack/Email)、构建工具(Maven/Gradle)等。
5分布式构建
通过主从(Master-Agent)架构在多台机器上并行执行任务,加速构建过程。
6.流水线(Pipeline)
使用 Groovy 语法编写复杂的多阶段流程(构建→测试→部署)。
支持声明式(Declarative)和脚本式(Scripted)语法。
1.1.2核心概念
Job(任务):定义一个自动化流程(如构建项目)。
Pipeline(流水线):将多个任务串联成完整的 CI/CD 流程。
Node(节点):执行任务的机器(主节点或代理节点)。
Workspace(工作空间):任务执行时的本地目录。
插件(Plugin):扩展 Jenkins 功能的附加组件。
1.1.3优势
开源免费:社区活跃,企业可低成本使用。
跨平台:支持 Windows、Linux、macOS。
高度可扩展:通过插件适应不同技术栈(Java、Python、Node.js 等)。
可视化界面:提供详细的构建日志和测试报告。
1.1.4典型应用场景
开发阶段:代码提交后自动运行单元测试。
测试阶段:部署到测试环境并执行自动化测试(Selenium)。
发布阶段:生成可执行包并发布到生产环境。
监控阶段:集成 Prometheus 监控构建健康状态。
1.1.5快速入门
1安装
下载 Jenkins WAR 包或使用 Docker 运行:
bash
docker run -p 8080:8080 jenkins/jenkins:lts
2.初始化
访问http://localhost:8080,按向导完成配置。
3.创建第一个任务
新建一个自由风格(Freestyle)任务或 Pipeline 任务。
1.1.6对比其他工具
1.1.7总结
Jenkins 是 DevOps 的核心工具之一,尤其适合需要高度自定义 CI/CD 流程的团队。虽然配置稍复杂,但其强大的功能和社区支持使其成为企业级自动化部署的首选。
1.2SonarQube介绍
1.2.1 SonarQube 介绍
SonarQube是一个开源的代码质量管理平台,用于持续检测代码中的 bugs(缺陷)、漏洞(Vulnerabilities)和代码异味(Code Smells)。它支持多种编程语言,帮助开发团队提高代码质量,确保符合最佳实践和安全标准。
1.2.2核心功能
静态代码分析(Static Code Analysis)
无需运行代码,直接扫描源代码,发现潜在问题。
支持30+ 种语言(Java、Python、C#、JavaScript、Go 等)。
问题分类
ØBugs(缺陷):可能导致运行时错误的代码(如空指针异常)。
ØVulnerabilities(漏洞):安全风险(如 SQL 注入、硬编码密码)。
ØCode Smells(代码异味):可维护性问题(如重复代码、过长方法)。
质量门(Quality Gate)
定义代码质量阈值(如覆盖率 ≥80%、零严重漏洞),未达标则阻止部署。
技术债务管理
量化代码问题修复所需时间,帮助团队优化技术债。
与 CI/CD 集成
支持Jenkins、GitLab CI、Azure DevOps等,在流水线中自动执行扫描。
多维度报告
提供可视化仪表盘,展示代码覆盖率、重复率、复杂度等指标。
1.2.3核心概念
1.2.4优势
✅多语言支持:覆盖主流编程语言和框架。
✅深度安全检测:集成 OWASP Top 10、CWE 等安全标准。
✅DevOps 友好:与 Jenkins、GitHub Actions 等无缝集成。
✅历史趋势分析:跟踪代码质量随时间的变化。
✅开源版免费:社区版支持基础功能,企业版提供高级特性(如 LDAP 认证)。
1.2.5典型工作流程
开发阶段:开发者在本地或 CI 中运行 SonarQube 扫描。
代码审查:通过 PR/MR 检查 SonarQube 报告,确保新代码符合质量标准。
质量门拦截:如果未达标(如存在严重漏洞),CI 流水线自动失败。
修复问题:团队根据优先级修复问题,降低技术债务。
1.2.6快速入门
1安装 SonarQube
Docker 方式(推荐):
bash
docker run -d --name sonarqube -p 9000:9000 sonarqube:lts-community
访问http://localhost:9000,默认账号:admin/admin。
扫描代码
使用 SonarScanner(以 Java 项目为例):
bash
sonar-scanner \
-Dsonar.projectKey=my-project\
-Dsonar.sources=.\
-Dsonar.host.url=http://localhost:9000\
-Dsonar.login=your-token
- 查看报告
在 SonarQube 网页端查看分析结果:
https://docs.sonarqube.org/latest/images/server-home-page.png
1.2.7与 Jenkins 集成示例
在 Jenkins Pipeline 中添加 SonarQube 扫描步骤:
groovy
pipeline {
agent any
stages {
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('SonarQube-Server') {
sh 'mvn clean verify sonar:sonar'
}
}
}
}
}
1.2.8对比其他工具
1.2.9总结
SonarQube 是提升代码质量和安全性的关键工具,尤其适合长期维护的项目。通过自动化扫描 + 质量门控,它能有效减少技术债务,帮助团队交付更可靠的软件。
2 Jenkins的安装
持续集成是DevOps很重要的工作,而Jenkins是持续集成使用最广泛的工具。在单元测试中,本地代码测试就绪后,需要把代码库(比如GitHub)中的代码检出到本地,测试没有问题后才可以再把代码检入到代码库中,从而保证代码库中的代码质量。这个工作可以由Jenkins工具来完成。本节介绍Jenkins的安装和基本配置。
2.1 Jenkins安装
1)最新版本的Jenkins(本书使用的版本为V2.523)需要在Java 17环境中使用。
2)下载jenkins.war包。
3)在环境配置变量中配置%JENKINS_HOME%。(在Windows环境下,%JENKINS_HOME%默认在C:\Users\.jenkins目录下;在Linux环境下,%JENKINS_HOME%默认在/root/.jenkins目录下)。
4)在jenkins.war文件目录下,通过命令“java -jar jenkins.war”启动Jenkins。
5)启动后,打开浏览器,输入“http://127.0.0.1:8080”。
6)将“%JENKINS_HOME%\secrets\initialAdminPassword”中的初始化密码填入到网页中。
7)按照提示安装所建议的插件。
2.2 Jenkins基本配置
1)打开Jenkins主界面,选择菜单“Manage Jenkins→Configure System”。
2)查看主目录是否正确,如下图所示。
3)配置Jenkins URL和系统管理员邮件地址,如图4-67所示。
4)选择菜单“Manage Jenkins→Global Tool Configuration”配置JDK信息,如2下图所示,注意不要选择“Install automatically”。
5)选择菜单“Manage Jenkins→Global Tool Configuration”配置Maven信息,如下图所示。
mvn-3.9.6:Maven的名称,使用时,配置信息必须与此一致。
C:\apache\apache-maven-3.8.6:Maven的主目录。
6)选择菜单“Manage Jenkins→Manage Plugins”进入插件管理。其中,Updates为需要更新的插件,Available plugins为可以安装的插件,Installed plugins为已经安装的插件,Advanced settings为高级设置,如下图所示。
7)“Advanced settings”的“Deploy Plugin”为安装插件的高级设置,选择相应的插件.hpi文件,单击【Deploy】按钮,即可安装,如图所示。
2.3 在Jenkins中建立项目
在Jenkins中,有多种方式创建项目,本文尽量通过流水线的方式来创建,因为通过这种方式可以把配置方法作为一个文件进行交流,更便于开展DevOps工作。
3.SonarQube的安装
3.1 安装PostgreSQL
安装Linux版本的PostgreSQL或Windows版本的PostgreSQL。如果是Windows版本的PostgreSQL,建立Sonar数据库。
3.2 安装SonarQube
3.2.1Windows下安装SonarQube
1)将“%SONAQUBE_HONE%\conf\sonar.properties”文件中相应部分更改:
text
sonar.jdbc.url=jdbc:postgresql://localhost:5432/sonar?currentSchema=public
sonar.jdbc.username=postgres
sonar.jdbc.password=123456
其中,sonar.jdbc.username=postgres与sonar.jdbc.password=123456分别为PostgreSQL的登录名和密码。
2)在命令行中输入:
bash
C:\>%SONAQUBE_HONE%\bin\windows-x86-64\StartSonar.bat
3)通过浏览器打开“http://192.168.0.123:9000”(192.168.0.123为SonarQube所在的主机IP地址),用admin/admin登录,如图示。注意:登录后需要修改密码(密码需要大小写字母,数字与特殊字符的组合)。
3.2.2.Linux下安装SonarQube
1)编辑“/etc/sysctl.conf”文件,配置如下参数:
bash
root@ubuntu:/home/jerry#gedit /etc/sysctl.conf
将vm.max_map_count设置为26144,fs.file-max设置为65536:
text
vm.max_map_count=262144
fs.file-max=65536
2)将SonarQube文件拷贝到/opt/目录下,创建sonar用户:
bash
root@ubuntu:/opt#sysctl -p
root@ubuntu:/opt #useradd sonar
root@ubuntu:/opt#passwd sonar
Enter new UNIX password://输入sonar用户的密码
Retype new UNIX password: //再次输入sonar用户的密码
passwd:password updated successfully
root@ubuntu:/opt#chown -R sonar:sonar /opt/sonarqube-9.6.1.59531/
3)编辑“/%SONARQUBE_HOME%/conf/sonar.properties”文件:
text
sonar.jdbc.url=jdbc:postgresql://localhost:5432/sonar?currentSchema=public
sonar.jdbc.username=sonar
sonar.jdbc.password=123456
其中
jdbc:postgresql://localhost:5432/sonar?currentSchema=public中的sonar为数据库名。
sonar.jdbc.username=sonar中的sonar为sonar用户名。
sonar.jdbc.password=123456中的123456为上一步设置的sonar用户名的密码。
4)配置/etc/security/limits.conf:
bash
root@ubuntu:/home/jerry#gedit /etc/security/limits.conf
ulimit -n 65536
ulimit -u 2048
5)用sonar用户登录,启动SonarQube:
bash
root@ubuntu:/home/jerry#su sonar
sonar@ubuntu:/home/jerry#ulimit -Hn
sonar@ubuntu:/#/%SONARQUBE_HOME%/bin/linux-x86-64/sonar.sh console
注意:在Linux中,SonarQube不可以用root用户启动。
6)通过浏览器打开“http://192.168.0.123:9000”(192.168.0.123为SonarQube所在的主机IP地址),用admin/admin登录,如图所示,登录后需要修改密码。
3.2.3 SonarQube的目录结构
SonarQube的目录结构如表所示。
3.2.4配置中文版本
首先通过SonarQube菜单“配置→应用市场”安装Chinese Pack,然后将下载的jar包放在%SONARQUBE_HOME%\extensions\plugins目录下,重新启动SonarQube。
这时,重新登录SonarQube后,界面会变为中文,再进入“配置→应用市场”界面,如图所示。
4 Java与Jenkins和SonarQube
4.1在Jenkins基本配置
4.1.1JDK在Jenkins配置
如图在Manage Jenkins/Tools配置
4.1.2 Maven在Jenkins配置
如图在Manage Jenkins/Tools配置
4.1.3 Jenkins Location配置
在Manage Jenkins System
4.1.4 设置字体
在Manage Jenkins System
4.2 在Jenkins中其他配置
4.2.1 JUnit5在Jenkins中的应用
在Jerkins中配置JUnit环境的步骤如下。
1)如下配置pom.xml文件。
xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven-compiler-plugin-version>2.3.2</maven-compiler-plugin-version>
</properties>
…
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
…
</plugins>
</build>
…
<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
...
2)安装Jenkins JUnit插件,如图所示。
3)书写Pipeline脚本:
groovy
pipeline {
agent any
stages {
stage('Build'){
steps {
bat "mvn clean test"
}
}
}
post{
always{
junit testResults:"**/target/surefire-reports/*.xml"
}
}
}
bat:执行Windows命令;如果要运行Linux命令,则使用sh。
junit testResults:"*/target/surefire-reports/.xml":展示JUnit测试报告。
post:包含整个pipeline或阶段完成后附加的一些步骤,可选项。post可以同时包含多种条件块。下面是post的参数说明。
always:不论当前完成状态是什么,都执行。
changed:只要当前完成状态与上一次完成状态不同就执行。
fixed:上一次完成状态为失败或不稳定,当前完成状态为成功时执行。
regression:上一次完成状态为成功,当前完成状态为失败、不稳定或中止时执行。
aborted:当前执行结果是中止(一般是人为中止)状态时执行。
failure:当前完成状态为失败时执行。
success:当前完成状态为成功时执行。
unstable:当前完成状态为不稳定时执行。
cleanup:清理条件块。不管当前完成状态如何,在其他所有条件块被执行完成后都被执行。
Pipeline脚本的基本架构如下:
groovy
pipeline {
agent any
tools{
//工具名 '工具标识'
} // tools为可选项
stages {
stage('StageName1'){
steps {
…
}
}
stage('StageName2'){
steps {
…
}
}
stage('StageNamen'){
steps {
…
}
}
}
post{
//事后处理,比如展示测试报告
always{
}
}
}
4)查看JUnit测试报告,如图所示。
4.2.2 Allure JUnit5在Jenkins中的应用
Allure 是一个轻量级、灵活且多语言的测试报告工具,用于生成美观、交互式的测试报告。它最初由 Yandex 团队开发,现在已成为开源项目。
1.主要特点
多语言支持:支持 Java、Python、JavaScript、Ruby、PHP、.NET 等多种编程语言
丰富的可视化:提供直观的图表和图形展示测试结果
高度可定制:可以添加步骤、附件、描述等信息增强报告
历史趋势:支持保存历史记录并展示测试趋势
集成友好:可与 Jenkins、TeamCity 等 CI 工具集成
2.核心功能
测试分类:按特性、故事、严重程度等维度组织测试
步骤记录:详细记录测试执行步骤
附件支持:可附加截图、日志、视频等
环境信息:记录测试执行环境
缺陷跟踪:与问题跟踪系统集成
3.使用流程
在测试框架中添加 Allure 适配器
执行测试并生成原始数据
使用 Allure 命令行工具生成 HTML 报告
4.在Jerkins中配置Allure
在Jerkins中配置Allure环境的步骤如下。
1) 安装Allure软件,将bin目录设为path
bash
allure --version
2.34.1
2)安装Jenkins Allure插件,如图所示。
3)选择菜单“Manage Jenkins→Global Tool Configuration”,根据所示进行配置。
4)选择菜单“Manage Jenkins→Configure System”,按照图所示进行配置。
5)配置pom.xml
xml
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version> <!-- 或更高稳定版本 -->
<configuration>
<argLine>${
surefireArgLine}</argLine>
<testFailureIgnore>true</testFailureIgnore>
<includes>
<include>**/*Test.java</include>
<include>**/Test*.java</include>
<include>**/*Tests.java</include>
<include>**/*TestCase.java</include>
</includes>
<systemProperties>
<property>
<name>allure.results.directory</name>
<value>${
project.build.directory}/allure-results</value>
</property>
<property>
<name>allure.link.issue.pattern</name>
<value>https://your-issue-tracker.com/issue/{
}</value>
</property>
</systemProperties>
</configuration>
…
注意
<property>
<name>allure.results.directory</name>
<value>${
project.build.directory}/allure-results</value>
</property>
<property>
<name>allure.link.issue.pattern</name>
<value>https://your-issue-tracker.com/issue/{
}</value>
</property>
以及
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-junit5</artifactId>
<version>2.29.1</version>
<scope>test</scope>
</dependency>
6)书写Pipeline脚本
groovy
pipeline {
agent any
tools{
maven 'mvn-3.9.6'
}
stages{
stage('junit'){
steps {
bat "mvn clean test"
}
}
}
post{
always{
junit testResults:"**/target/surefire-reports/*.xml"
script{
allure([
includeProperties:false,
jdk:'',
properties:[],
reportBuildPolicy:'ALWAYS',
results:[[path:'target/surefire-reports']]
])
}
}
}
}
其中,maven 'mvn-3.9.6'为配置的Maven Name值,表示使用maven工具。script后面的代码表示测试完毕展示Allure报告。关于Allure的具体配置,可以查看网上的资料。
7)运行完毕,如图所示,单击“Allure Report”展示Allure报告。
4.2.3 JaCoCo在Jenkins中的应用
JaCoCo (Java Code Coverage) 是一个开源的 Java 代码覆盖率分析工具,广泛用于测量单元测试对源代码的覆盖程度。
1.核心特性
多维度覆盖率分析:
行覆盖率 (Line Coverage)
分支覆盖率 (Branch Coverage)
方法覆盖率 (Method Coverage)
类覆盖率 (Class Coverage)
指令覆盖率 (Instruction Coverage)
圈复杂度 (Cyclomatic Complexity)
.轻量级:作为 Java 代理运行,无需对源代码进行修改
多种集成方式:
命令行工具
Ant 任务
Maven 插件
Gradle 插件
Eclipse EclEmma 插件
2.工作原理
JaCoCo 通过以下方式收集覆盖率数据
插桩(Instrumentation):在字节码层面插入探针
执行测试:运行测试时记录执行路径
生成报告:分析收集的数据生成可视化报告
3.主要使用场景
持续集成环境中的代码质量门禁
开发过程中检查测试充分性
识别未被测试覆盖的代码区域
监控测试覆盖率趋势
4.报告解读
JaCoCo 生成的 HTML 报告包含:
覆盖率概览 (所有包和类的汇总数据)
源代码高亮显示 (绿色表示已覆盖,红色表示未覆盖)
分支覆盖详情
方法覆盖详情
圈复杂度分析
5.优势
零侵入性:不需要修改源代码
高性能:对应用性能影响极小
丰富的报告格式:支持 HTML、XML、CSV 等多种格式
阈值检查:可设置覆盖率最低要求,构建失败条件
与构建工具深度集成:Maven、Gradle、Ant 等
JaCoCo 是 Java 生态中最流行的代码覆盖率工具之一,特别适合需要监控和提升代码质量的 Java 项目。
- JaCoCo在Jenkins中配置
1)配置pom.xml文件。
xml
<!--Jacoco -->
…
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.9</version>
<executions>
<execution>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>${
project.build.directory}/jacoco.exec</destFile>
<propertyName>surefireArgLine</propertyName>
<!-- 添加以下配置 -->
<append>true</append>
</configuration>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
2)在Jenkins中安装JaCoCo插件,如所示。
3)创建pipeline脚本
groovy
…
stage('jacoco') {
steps {
// 只需要执行report目标,测试已在Build阶段完成
jacoco(
execPattern: 'target/**/*.exec',
classPattern: 'target/classes',
sourcePattern: 'src/main/java'
)
}
}
…
post{
…
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'target/site/jacoco',
reportFiles:'index.html',
reportName:'jacoco Reports',
reportTitles:'jacoco Report'])
}
4)运行,获得测试结果,如图所示。
Jenkins JaCoCo的概要测试报告
5)单击图标,查看报告的详细信息,如图所示。
4.2.4PMD在Jerkins的应用
PMD 深入解析:静态代码分析利器
PMD 核心定位
PMD 是一款专注于源代码级别分析的静态检测工具,通过解析抽象语法树(AST)深度扫描代码结构,能在不运行程序的情况下发现:编码缺陷(如空指针风险、资源未关闭)
风格违规(如命名不规范、冗余代码)
设计问题(如过长方法、过度耦合)
安全漏洞(如硬编码密码、XSS风险)
2.技术架构剖析
1)语言解析层
支持多语言解析器:
Java(基于JavaCC)
Apex(Salesforce专属语言)
JavaScript(配合Rhino引擎)
PL/SQL(Oracle数据库脚本)
生成AST供规则引擎分析
2)规则引擎
XPath规则:通过XML定义代码模式匹配规则(示例):
xml
<rule name="AvoidDoubleBraceInitialization">
<description>禁止使用双括号初始化</description>
<priority>3</priority>
<xpath>//AllocationExpression[ClassOrInterfaceBody]</xpath>
</rule>
.Java规则:通过继承AbstractJavaRule实现复杂检测逻辑
3)扩展机制
支持开发自定义规则(JAR包形式加载)
插件化架构,可扩展新语言支持
3.企业级应用方案
1)CI/CD 集成范例(Jenkins Pipeline)
groovy
pipeline {
agent any
stages {
stage('Static Analysis') {
steps {
sh 'mvn pmd:pmd'// Maven项目执行分析
pmd canComputeNew: false,
pattern: '**/pmd.xml',
healthy: 10,
unHealthy: 100// 质量门禁
}
}
}
2)关键质量指标(KQI)监控
sql
-- 结合SonarQube存储分析结果
SELECT
project_key,
COUNT(*) AS violations,
SUM(CASE WHEN severity = 'BLOCKER' THEN 1 ELSE 0 END) AS blockers
FROM pmd_issues
GROUP BY project_key
ORDER BY blockers DESC;
- 高级使用技巧
1)增量分析(仅扫描变更代码)
sql
-- 结合SonarQube存储分析结果
SELECT
project_key,
COUNT(*) AS violations,
SUM(CASE WHEN severity = 'BLOCKER' THEN 1 ELSE 0 END) AS blockers
FROM pmd_issues
GROUP BY project_key
ORDER BY blockers DESC;
2)自定义规则开发
java
public class CustomStreamRule extends AbstractJavaRule {
@Override
public Object visit(ASTMethodDeclaration node, Object data) {
if (node.getMethodName().contains("Stream")) {
addViolation(data, node, "避免使用Stream命名方法");
}
return super.visit(node, data);
}
}
3)基准测试(性能优化)
text
# 使用--benchmark参数
pmd check -d src/ --benchmark -R rulesets/java/quickstart.xm
行业对比深度分析
6.最佳实践建议
渐进式引入
初期只启用category/java/errorprone.xml核心规则
逐步增加design.xml等规则集
技术债管理
bash
# 使用@SuppressWarnings注解临时豁免
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public class Demo {
... }
团队协作方案
将规则配置文件ruleset.xml纳入版本控制
使用Git pre-commit hook触发基础检查
7.前沿发展方向
AI增强:正在试验基于机器学习的误报过滤
多语言融合:增强对Kotlin/Swift的支持
云原生方案:提供SaaS化分析服务(PMD Cloud)
8.官方资源矩阵
规则库: https://docs.pmd-code.org/
学术论文:《AST-Based Source Code Analysis with PMD》(IEEE SANER 2020)
企业支持:提供商业版PMD Pro(含优先级支持)
PMD 特别适合追求高效代码审查和深度质量管控的技术团队,其平衡了检测深度与执行效率,是现代DevOps工具链中不可或缺的一环。建议从核心规则集入手,逐步构建定制化的代码质量防护体系。
- Jerkins下安装PMD
1)简单配置
①在Jerkins下安装Violations插件。
②配置pom.xml文件。
xml
<!-- PMD 代码检查 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.27.0</version>
<configuration>
<failOnViolation>false</failOnViolation>
<printFailingErrors>true</printFailingErrors>
<rulesets>
<ruleset>/rulesets/java/quickstart.xml</ruleset>
</rulesets>
<excludes>
<exclude>**/generated/**/*.java</exclude>
<exclude>**/test/**/*.java</exclude>
</excludes>
<outputDirectory>${
project.build.directory}/site/</outputDirectory>
<format>html</format>
<targetJdk>17</targetJdk>
<linkXRef>true</linkXRef>
<minimumTokens>100</minimumTokens> <!-- 可选:设置最小代码量阈值 -->
</configuration>
</plugin>
<!-- 必需:交叉引用支持 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>3.3.0</version>
</plugin>
JDK17 必须>3.20
③在Jenkins中创建Freestyle project。
④在“Build Steps”中选择“Execute Windows batch command”。
⑤输入命令:
mvn pmd:pmd
⑥在“构建后操作”中选择“Report Violations”。
这时会出现多行数据,每行分别有四列:
第一列是“太阳”图标,是生成晴朗天气报告的此类违规次数最少的图标。例如,如果值为10,表示违规数目从0到10,生成晴天报告;11或更高则生成其他的图标。
第二列是“暴风雨”图标,是生成暴风雨天气报告的此类违规次数最少的图标。
第三列是“不稳定”图标,是导致构建不稳定类的违规数量。
第四列是XML文件名模式,这是一种Ant类型模式,用于匹配工作区中这种类型的冲突文件。多个模式之间用逗号分隔。
⑦在“Source encoding”中选择“UTF-8”。
⑧在PMD行中按照图所示输入,开始构建。
⑨构建完毕,单击图标,如图所示。
⑩单击图中的某个文件,查看详细信息,如图7-8所示(PMD软件自身的缺陷,这里中文显示了乱码)。
2)图形化报告配置
①构建pipeline脚本:
groovy
stage('PMD Analysis') {
steps {
script {
// 1. 先执行PMD检查
bat "mvn pmd:pmd pmd:cpd"
// 2. 单独生成站点报告(包含HTML格式)
bat "mvn site:site -DgenerateReport=true"
// 3. 验证报告生成
if (!fileExists('target/site/pmd.html')) {
echo "警告: 未找到HTML报告,尝试备用路径..."
// 多模块项目可能路径不同
if (fileExists('target/site/pmd-pmd.html')) {
bat 'copy target/site/pmd-pmd.html target/site/pmd.html'
} else {
error "PMD HTML报告生成失败"
}
}
}
}
}
post{
always{
junit testResults:"**/target/surefire-reports/*.xml"
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'target/site',
reportFiles:'pmd.html',
reportName:'Pmd Reports',
reportTitles:'Pmd Report'])
}
②运行,单击图标获得测试报告(建议用Firefox查看),如图所示。
4.2.5 SonarQube的应用
SonarQube 深度解析:企业级代码质量与安全管控平台
1.核心定位
SonarQube 是开源的代码质量持续监测平台,通过静态分析技术(SAST)实现:
多维度质量检测:代码缺陷、安全漏洞、技术债务、测试覆盖率
27+ 语言支持:Java/C#/Python/Go/JS/TS 等主流语言
DevOps 集成:深度对接 CI/CD 流程,实现质量门禁(Quality Gate)
2.核心功能架构
3.关键能力对比
4.技术集成方案
1) Jenkins Pipeline 集成
groovy
pipeline {
agent any
stages {
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('SonarQube-Server') {
sh 'mvn sonar:sonar -Dsonar.projectVersion=${BUILD_ID}'
}
}
}
stage("Quality Gate") {
steps {
timeout(time: 1, unit: 'HOURS') {
waitForQualityGate abortPipeline: true
}
}
}
2)自定义规则开发(Java示例)
java
@Rule(key = "AvoidSystemOut")
public class AvoidSystemOutRule extends IssuableSubscriptionVisitor {
@Override
public List<Tree.Kind> nodesToVisit() {
return Collections.singletonList(Tree.Kind.METHOD_INVOCATION);
}
@Override
public void visitNode(Tree tree) {
MethodInvocationTree mit = (MethodInvocationTree)tree;
if (mit.symbol().name().equals("println") &&
mit.symbol().owner().name().equals("System.out")) {
reportIssue(mit, "禁止使用System.out.println()");
}
}
}
5.企业级实践案例
1)某金融企业质量门禁配置(sonar-project.properties)
properties
# 关键质量阈值
sonar.qualitygate.wait=true
sonar.qualitygate.timeout=300
# 安全类指标
sonar.security.rating=1.0 (必须无高危漏洞)
sonar.security_review.rating=2.0
# 覆盖率要求
sonar.coverage.exclusions=**/test/**/*
sonar.java.coverage.minimum=80%
# 技术债务控制
sonar.debt.ratio.threshold=5%
6.性能优化技巧
1)增量扫描(仅分析变更文件)
bash
sonar.scanType=incremental+
2)排除测试代码
properties
sonar.exclusions=**/*Test.java,**/generated/**/*
3)分布式扫描
bash
sonar.scanner.memory=4096m # 分配更大内存
sonar.analysis.worker=4 # 多线程分析
7.与竞品技术对比
演进路线
v10.x:支持Java 17+、增强Clean Code概念
v11.x:内置AI辅助修复建议(SonarFix)
Cloud版:SAAS化服务(AWS/GCP集成)
8.官方资源
社区版下载:https://www.sonarsource.com/products/sonarqube/
规则库:https://rules.sonarsource.com/
学术论文:《Metrics-Based Software Quality Assessment in SonarQube》(IEEE ICSME 2021)
SonarQube 特别适合需要建立标准化质量体系的研发组织,建议从社区版入手,逐步演进到商业版实现企业级管控
- SonarQube配置
1)下载安装“SonarQube Scanner”插件。
2)通过菜单“Manage Jenkins→Manage Credentials”,选择“Stores scoped to Jenkins”下的“System”,如图所示。单击“Global credentials(unrestricted)”后,再单击图标添加凭据。
按照图所示的信息设置安全凭证。其中,Secret为SonarQube提供的token值。
3)通过菜单“Manage Jenkins→Configure System→SonarQube servers”对SonarQube servers进行设置,如图所示。
http://192.168.0.123:9000:SonarQube的URL地址。
Server authentication token:选择第2)步设置的安全凭证的名称。
4) 由于SonarQube网络调用不支持192.168.X.X、127.0.0.1和localhost回路地址,必须先下载Linux或Windows版本的nginx。配置conf/nginx.conf
text
server {
listen 80;
server_name sonar-webhook-proxy;
location /sonarqube-webhook {
proxy_pass http://localhost:8090/sonarqube-webhook;
proxy_set_header Host $host;
}
启动nginx.exe(nginx)文件
5)在SonarQube中,通过菜单“配置→配置→网络调用”创建网络调用:http://sonar-webhook-proxy/sonarqube-webhook/,如图所示。
6)创建pipeline 脚本
groovy
pipeline {
agent any
tools{
maven 'mvn-3.8.6'
}
stages{
stage('Code Analysis'){
steps{
withSonarQubeEnv('SonarQube'){
bat'''
mvn clean verify sonar:sonar\
-Dsonar.projectKey=Java \
-Dsonar.host.url=http://127.0.0.1:9000
'''
}
}
}
stage('Quality Gate'){
steps{
script {
timeout(time:1,unit:'HOURS'){
sleep(5)
def qg = waitForQualityGate()
if (qg.status != 'OK'){
echo "Status:${qg.status}"
error "Pipeline aborted due to quality gate failure:${qg.status}"
}
}
}
}
}
}
}
注意:“withSonarQubeEnv('SonarQube'){”中的SonarQube必须与图7-74中创建的name值保持一致。
在Jenkins中使用SonarQube进行扫描,不要使用“-Dsonar.login=admin \”和“-Dsonar.password=123456 \”。
4.3例子
4.3.1 版本
PMD version: 7.14.0
allure version: 2.34.1
Jenkins:Version 2.523
SonarQube:25.8.0.112029
PMD:6.52.0
JaCoCo:0.8.9
最后用我将要出版的《带着ChatGPT玩转软件开发》作为案例
4.3.2 pom.xml配置
xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ebusiness</groupId>
<artifactId>ChatGPTEbusiness</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.plugin.version>3.11.0</maven.compiler.plugin.version>
<junit.platform.version>1.9.3</junit.platform.version>
<junit.jupiter.version>5.10.0</junit.jupiter.version>
<surefire.version>3.1.2</surefire.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${
surefire.version}</version>
<configuration>
<argLine>
-Xshare:off
-Djdk.attach.allowAttachSelf=true
${
surefireArgLine}
</argLine>
</configuration>
</plugin>
<!-- PMD 代码检查 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.27.0</version>
<configuration>
<failOnViolation>false</failOnViolation>
<printFailingErrors>true</printFailingErrors>
<rulesets>
<ruleset>/rulesets/java/quickstart.xml</ruleset>
</rulesets>
<excludes>
<exclude>**/generated/**/*.java</exclude>
<exclude>**/test/**/*.java</exclude>
</excludes>
<outputDirectory>${
project.build.directory}/site/</outputDirectory>
<format>html</format>
<targetJdk>17</targetJdk>
<linkXRef>true</linkXRef>
<minimumTokens>100</minimumTokens>
</configuration>
</plugin>
<!-- 必需:交叉引用支持 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<!--Jacoco -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.9</version>
<executions>
<execution>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>${
project.build.directory}/jacoco.exec</destFile>
<propertyName>surefireArgLine</propertyName>
<!-- 添加以下配置 -->
<append>true</append>
</configuration>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version> <!-- 存在安全漏洞 -->
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20210307</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea</artifactId>
<version>1.1.14</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.16</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.68</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-reflect</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<!-- JUnit 5 基础依赖 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${
junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${
junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-junit5</artifactId>
<version>2.29.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
4.2.3 pipeline的书写
1 pipeline代码
注意顺序
groovy
pipeline {
agent any
tools{
maven 'mvn-3.9.6'
}
stages {
stage('Build'){
steps {
bat "mvn clean test"
}
}
stage('Code Analysis'){
steps{
withSonarQubeEnv('SonarQube'){
bat '''
mvn clean verify sonar:sonar\
-Dsonar.projectKey=Java \
-Dsonar.host.url=http://127.0.0.1:9000
'''
}
}
}
stage('Quality Gate'){
steps{
script {
timeout(time:5,unit:'MINUTES'){
sleep(5)
def qg = waitForQualityGate()
if (qg.status != 'OK'){
echo "Status:${qg.status}"
error "Pipeline aborted due to quality gate failure:${qg.status}"
}
}
}
}
}
stage('jacoco') {
steps {
// 只需要执行report目标,测试已在Build阶段完成
jacoco(
execPattern: 'target/**/*.exec',
classPattern: 'target/classes',
sourcePattern: 'src/main/java'
)
}
}
stage('PMD Analysis') {
steps {
script {
// 1. 先执行PMD检查
bat "mvn pmd:pmd pmd:cpd"
// 2. 单独生成站点报告(包含HTML格式)
bat "mvn site:site -DgenerateReport=true"
// 3. 验证报告生成
if (!fileExists('target/site/pmd.html')) {
echo "警告: 未找到HTML报告,尝试备用路径..."
// 多模块项目可能路径不同
if (fileExists('target/site/pmd-pmd.html')) {
bat 'copy target/site/pmd-pmd.html target/site/pmd.html'
} else {
error "PMD HTML报告生成失败"
}
}
}
}
}
}
post{
always{
junit testResults:"**/target/surefire-reports/*.xml"
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'target/site',
reportFiles:'pmd.html',
reportName:'Pmd Reports',
reportTitles:'Pmd Report'])
}
script{
publishHTML (target :[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'target/site/jacoco',
reportFiles:'index.html',
reportName:'jacoco Reports',
reportTitles:'jacoco Report'])
}
script{
allure([
includeProperties:false,
jdk:'',
properties:[],
reportBuildPolicy:'ALWAYS',
results:[[path:'target/surefire-reports']]
])
}
}
}
}
2最后结果
3单元测试结果
4 Allure Report
5 Jacoco Report
6 PMD Report
5 Python与Jenkins和SonarQube
5.1安装
代码质量分析工具
pip3 install pylint
代码覆盖率分析工具
pip3 install pytest-cov
运行pysonar的时候用
pip install pysonar
运行pylint报告的时候用
pip install pylint pylint-html
5.2unittest
5.2.1框架概述
unittest是Python标准库自带的单元测试框架(又称PyUnit),受到Java的JUnit启发而设计。它提供了一套完整的测试解决方案,是Python生态中最基础的测试工具。
5.2.2核心特性
1主要功能
✔测试自动化-自动发现和执行测试用例
✔丰富的断言方法-提供多种断言方式验证结果
✔测试固件(Fixtures)-支持setUp/tearDown机制
✔测试套件(TestSuites)-灵活组织测试用例
✔跳过测试和预期失败-灵活控制测试执行
✔丰富的命令行接口-支持多种测试执行方式
2断言方法
5.2.3基本用法
1简单测试用例
python
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(),'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
if__name__=='__main__':
unittest.main()
2测试固件
python
class TestDatabase(unittest.TestCase):
@classmethod
def setUpClass(cls):
#整个测试类执行前运行一次
cls.db=connect_to_db()
def setUp(self):
#每个测试方法前运行
self.cursor=self.db.cursor()
def test_query(self):
self.cursor.execute("SELECT1")
self.assertEqual(self.cursor.fetchone(),(1,))
def tearDown(self):
#每个测试方法后运行
self.cursor.close()
@classmethod
def tearDownClass(cls):
#整个测试类执行后运行一次
cls.db.close()
3跳过测试
python
class SkipTests(unittest.TestCase):
@unittest.skip("演示跳过")
def test_nothing(self):
self.fail("不应该执行")
@unittest.skipIf(sys.platform=="win32","不在Windows运行")
def test_linux_feature(self):
#Linux特有功能测试
pass
5.2.4.高级用法
1测试套件
python
def suite():
suite=unittest.TestSuite()
suite.addTest(TestStringMethods('test_upper'))
suite.addTest(TestDatabase('test_query'))
return suite
if__name__=='__main__':
runner=unittest.TextTestRunner()
runner.run(suite())
2参数化测试
python
#需要安装parameterized扩展
from parameterized import parameterized
classTestMath(unittest.TestCase):
@parameterized.expand([
(1,1,2),
(2,3,5),
(-1,-1,-2)
])
def test_add(self,a,b,expected):
self.assertEqual(a+b,expected)
3自定义测试运行器
python
class MyTestRunner(unittest.TextTestRunner):
def run(self,test):
print("===开始测试===")
result=super().run(test)
print(f"===测试完成:{result}")
return result
5.2.5命令行使用
bash
#运行单个测试模块
python -m unittesttest_module
#运行特定测试类
python -m unittesttest_module.TestClass
#运行单个测试方法
python -m unittesttest_module.TestClass.test_method
#发现并运行所有测试
python -m unittestdiscover
#常用选项
-v#详细输出
-f#遇到失败时停止
--locals#显示局部变量
5.2.6最佳实践
测试命名:测试方法以test_开头
测试隔离:每个测试应该是独立的
避免过度断言:一个测试验证一个行为
合理使用固件:setUp/tearDown管理测试环境
结合覆盖率工具:使用coverage.py测量测试覆盖率
5.2.7与其他框架对比
5.2.8.适用场景
✅标准库和框架的测试
✅需要与Python版本严格兼容的项目
✅已有unittest测试遗产的项目
✅需要最小依赖的测试环境
unittest作为Python标准库的一部分,特别适合需要避免第三方依赖的项目,以及作为学习Python测试的入门框架。
5.3pytest
pytest是Python最流行的测试框架之一,以其简洁的语法和强大的功能而闻名。它完全兼容unittest,同时提供了更优雅的编写方式和更丰富的功能。
5.3.1.核心优势
✔简洁的断言语法-直接使用Python的assert语句
✔自动发现测试-无需繁琐的测试套件配置
✔丰富的插件系统-超过1000个插件扩展功能
✔参数化测试支持-轻松实现多组数据测试
✔详细的失败信息-自动显示变量值帮助调试
✔兼容unittest-可以运行现有的unittest测试用例
5.3.2.基本用法
1简单测试示例
python
#test_sample.py
def func(x):
return x+1
def test_answer():
assert func(3)==5#这条测试会失败
运行测试:
bash
pytest test_sample.py
2测试类和固件
python
class TestClass:
def test_one(self):
x="hello"
assert "h" in x
def test_two(self):
x="hello"
assert hasattr(x,"check")#会失败
3固件(Fixtures)
python
import pytest
@pytest.fixture
def sample_data():
return [1,2,3,4,5]
def test_sum(sample_data):
assert sum(sample_data)==15
5.3.3.高级特性
1参数化测试
python
import pytest
@pytest.mark.parametrize("input,expected",[
(3,5),
(5,7),
(10,12),
])
def test_increment(input,expected):
assert input+2==expected
2临时文件和目录
python
def test_create_file(tmp_path):
d=tmp_path/"sub"
d.mkdir()
p=d/"hello.txt"
p.write_text("content")
assert p.read_text()=="content"
3标记和筛选测试
python
@pytest.mark.slow
def test_long_running():
import time
time.sleep(5)
assert true
#只运行标记为slow的测试
pytest-mslow
5.3.4.插件生态系统
安装插件:
bash
pip install pytest-covpytest-xdist
5.3.5.与unittest比较
5.3.6.最佳实践
命名规范:测试文件以test开头,测试函数以test开头
保持测试独立:测试之间不应有依赖关系
合理使用固件:共享测试准备代码
利用参数化:减少重复测试代码
结合覆盖率工具:确保测试充分性
5.3.7.典型工作流程
bash
#安装
pip install pytest
#运行所有测试
pytest
#运行特定测试
pytest path/to/test_file.py::test_function
#生成覆盖率报告
pytest --cov=myprojecttests/
#并行运行测试
pytest -n4
pytest以其简洁性和强大功能成为Python测试的事实标准,特别适合从简单项目到大型企业应用的测试需求。它的设计哲学是让编写测试变得简单有趣,同时保持足够的灵活性来处理复杂的测试场景。
5.3.8 在Jenkins中使用
groovy
stages {
stage('Run Tests') {
steps {
script {
bat """
${
PYTHON} -m pytest -v --alluredir=${
ALLURE_RESULTS}
"""
}
}
}
…
post {
always {
script {
allure([
includeProperties: false,
jdk: '',
properties: [],
reportBuildPolicy: 'ALWAYS',
results: [[path: "${ALLURE_RESULTS}"]]
])
}
5.4 flake8
Flake8是一个流行的Python代码质量检查工具,它集成了多个静态分析工具,用于检查代码是否符合PEP8风格指南并识别常见错误。
5.4.1.核心组件
Flake8实际上是以下三个工具的封装:
PyFlakes:检查语法错误和未使用的变量/导入
pycodestyle(原PEP8):检查PEP8代码风格违规
McCabe:检查代码复杂度(可选)
5.4.2.主要功能
✔代码风格检查(PEP8合规性)
✔语法错误检测(如未使用的变量)
✔代码复杂度分析(防止过于复杂的函数)
✔可配置性强(支持自定义规则)
✔插件系统(可扩展额外检查)
5.4.3.基本使用
1安装
bash
pip install flake8
2运行检查
bash
#检查当前目录
flake8
#检查特定文件
flake8 myfile.py
#检查并显示错误统计
flake8 --statistics
3典型输出
text
./example.py:1:1:F401'os'importedbutunused
./example.py:3:5:E225missingwhitespacearoundoperator
./example.py:5:1:E302expected2blanklines,found1
5.4.4.错误代码解释
Flake8使用以下错误代码前缀:
5.4.5.配置选项
1配置文件(.flake8)
ini
[flake8]
ignore=E203,E266,E501
max-line-length=88
max-complexity=10
exclude=.git,__pycache__,migrations
2常用命令行选项
bash
--ignore=E501,W503#忽略特定错误
--max-line-length=120#设置最大行长度
--max-complexity=5#设置最大复杂度阈值
--exclude=migrations#排除目录
--format=html#HTML格式输出
5.4.6.插件生态系统
Flake8支持丰富的插件扩展功能
安装插件:
bash
pip install flake8-docstrings flake8-builtins
5.4.7.集成开发环境支持
Flake8可以与多种工具集成:
VSCode:通过Python扩展自动支持
PyCharm:配置为外部工具
GitHooks:提交前自动检查
CI/CD:作为质量门禁
5.4.8.与同类工具对比
5.4.9.最佳实践
项目初期配置:建立统一的.flake8配置文件
与pre-commit集成:提交代码前自动检查
合理设置忽略规则:避免过度严格
结合自动格式化工具:如Black+Flake8组合
.CI流水线集成:设置质量门禁
5.4.10.示例工作流
bash
#安装
pip install flake8 flake8-docstrings
#配置.flake8文件
echo-e"[flake8]\n max-line-length=120\n ignore=E203\n">.flake8
#运行检查
Flake 8my_project/
#与pre-commit集成
echo -e"repos:\n-repo:https://github.com/PyCQA/flake8\nrev:3.9.2\nhooks:\n-id:flake8">.pre-commit-config.yaml
Flake8是Python开发者工具箱中不可或缺的代码质量守护者,特别适合中小型项目快速建立代码规范检查机制。它的轻量级设计和可扩展性使其成为团队协作开发中的理想选择。
5.5pylint
Pylint是一个功能强大的Python源代码分析器,它不仅能检查代码风格(PEP8),还能检测编程错误、提供重构建议,并评估代码质量。
5.5.1.核心特性
1主要功能
✔代码风格检查(PEP8合规性)
✔错误检测(未使用变量、未定义变量等)
✔代码复杂度分析(函数/类复杂度评分)
✔类型检查(与类型提示结合使用)
✔可定制规则(支持禁用/修改特定检查)
✔质量评分系统(10分制代码质量评估)
2检查类别
5.5.2.基本使用
1安装
bash
pip install pylint
2运行检查
bash
#检查单个文件
pylint mymodule.py
#检查整个包
pylint mypackage/
#生成报告
pylint --output-format=html mymodule.py>report.html
3典型输出
text
*************Modulemymodule
mymodule.py:1:0:C0114:Missing module docstring(missing-module-docstring)
mymodule.py:5:4:W0612:Unused variable 'x'(unused-variable)
mymodule.py:10:0:C0116:Missing function docstring(missing-function-docstring)
Your code has been rated at 5.12/10
5.5.3.配置与定制
1配置文件(.pylintrc)
1)生成默认配置
bash
pylint --generate-rcfile>.pylintrc
2)常见配置选项:
ini
[MASTER]
ignore=test_,migrations#忽略特定文件/目录
[MESSAGESCONTROL]
disable=missing-docstring,invalid-name#禁用特定检查
[FORMAT]
max-line-length=120#行长度限制
2命令行常用选项
bash
--disable=C0114,W0613#禁用特定检查
--enable=similarities#启用额外检查
--reports=y#生成详细报告
--score=y#显示质量评分
5.5.4.高级功能
1类型检查
配合类型提示使用:
python
def add(a:int,b:int)->int:
return a+b #Pylint能检测类型不匹配问题
add("1",2) #会触发类型警告
2自定义检查器
创建插件
python
from pylint.checkers import BaseChecker
class MyChecker(BaseChecker):
name='custom'
msgs={
'W9999':(
'Avoid"foo"functions',
'avoid-foo',
'Usedwhenafunctionisnamed"foo"'
)}
def visit_functiondef(self,node):
if node.name=="foo":
self.add_message('avoid-foo',node=node)
def register(linter):
linter.register_checker(MyChecker(linter))
5.5.5.插件生态系统
安装插件:
bash
pip install lpylint-django
pylint --load-plugins pylint_django myapp
5.5.6.与同类工具对比
5.5.7.最佳实践
渐进式采用:初期禁用过于严格的规则
团队统一配置:共享.pylintrc文件
CI集成:设置质量评分阈值
与IDE集成:实时反馈问题
结合其他工具:如Black+isort+Pylint组合
5.5.8.集成示例
1 pre-commit配置
yaml
repos:
-repo:https://github.com/PyCQA/pylint
rev:v2.12.2
hooks:
-id:pylint
args:[--rcfile=.pylintrc]
2 CI配置示例
yaml
#GitHubActions
-name:RunPylint
run:|
pip install pylint
pylint --fail-under=7.0 mypackage/
5.5.9.优缺点分析
1优点
检查全面深入
提供质量量化指标
高度可配置
良好的IDE支持
2缺点
学习曲线较陡
运行速度较慢
部分规则过于严格
Pylint特别适合中大型Python项目的代码质量管控,能显著提高代码可维护性和一致性。虽然初始配置可能需要一些调整,但长期来看能为团队节省大量代码审查时间。
5.5.10 在Jenkins中使用
groovy
stage('pylint Code Quality') {
steps {
script {
bat "${PYTHON} -m pylint --output-format=json *.py > report.json 2>&1 || exit 0"
bat "pylint-json2html -o report.html report.json"
}
}
}
…
script {
publishHTML (target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.\\',
reportFiles: 'report.html',
reportName: 'pylint Reports',
reportTitles: 'pylint Report'])
}
5.6 pytest-cov
pytest-cov是pytest的一个插件,用于测量代码的测试覆盖率。它基于coverage.py,提供了与pytest框架无缝集成的覆盖率统计功能。
5.6.1.核心功能
✔语句覆盖率统计-检测哪些代码被执行
✔分支覆盖率分析-检查条件分支的覆盖情况
✔多种报告格式-支持终端、HTML、XML等输出
✔覆盖率阈值-设置最小覆盖率要求
✔源码标注-显示未覆盖的代码行
✔并行测试支持-与pytest-xdist兼容
5.6.2.基本使用
1安装
bash
pip install pytest-cov
2基本命令
bash
#运行测试并计算覆盖率
pytest --cov=myprojecttests/
#指定最小覆盖率阈值(如100%)
pytest --cov=myproject--cov-fail-under=100 tests/
#生成HTML报告
pytest --cov=myproject--cov-report=htmltests/
3典型输出
text
-----------coverage:platformlinux,python3.9.5-----------
NameStmtsMissCover
----------------------------------------
myproject/__init__.py 20 100%
myproject/module.py 102 80%
----------------------------------------
TOTAL 122 83%
5.6.3.高级配置
1配置文件(pytest.ini)
ini
[pytest]
addopts=--cov=myproject--cov-report=term-missing
cov_fail_under=90
cov_report=
term
html
2忽略代码块
python
#整个函数忽略
def uncovered_function(): #pragma:nocover
pass
#单行忽略
x=1 #pragma:nocover
3只检查新增代码
bash
pytest --cov=myproject --cov-branch --cov-append
5.6.4.报告类型
5.6.5.与其他工具集成
1与Coveralls集成
yaml
#.github/workflows/tests.yml
-name:Uploadcoverage
run:|
pip install coveralls
coveralls
env:
COVERALLS_REPO_TOKEN:${
{
secrets.COVERALLS_TOKEN}}
2与Codecov集成
bash
#生成报告后
bash<(curl-shttps://codecov.io/bash)
5.6.6.最佳实践
设置合理阈值:根据项目阶段调整--cov-fail-under
关注关键模块:核心代码应保持100%覆盖率
结合分支覆盖:使用--cov-branch检测条件分支
定期检查趋势:监控覆盖率变化
忽略合理部分:如测试代码、调试代码
5.6.7.常见问题
Q1:如何只显示覆盖率变化?
bash
pytest --cov=myproject --cov-report=term-missing:skip-covered
Q2:如何合并多个测试运行的覆盖率?
bash
pytest --cov=myproject --cov-append
Q3:为什么某些行显示为未覆盖?
可能是异常处理分支未测试
使用--cov-branch检查分支覆盖
pytest-cov是Python测试覆盖率分析的首选工具,特别适合需要精确控制测试质量的团队项目。它与pytest生态系统的深度集成使得覆盖率分析变得简单而高效。
5.6.8 在Jenkins中使用
groovy
stage('Run Coverage') {
steps {
script {
bat """
${
PYTHON} -m pytest --cov=./ --cov-report=html:site || echo "Coverage completed with warnings"
"""
}
}
}
…
post {
always {
script {
publishHTML (target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'site',
reportFiles: 'index.html',
reportName: 'Coverage Reports',
reportTitles: 'Coverage Report'])
}
5.7 Coverage
Coverage.py是Python生态中最主流的代码覆盖率测量工具,它可以精确统计测试执行过程中哪些代码被实际运行,哪些未被覆盖。
5.7.1.核心功能
✔语句覆盖率-检测代码行是否被执行
✔分支覆盖率-分析条件分支的覆盖情况
✔多种输出格式-支持终端、HTML、XML等报告
✔插件系统-可与pytest、Django等框架集成
✔并行测试支持-适合分布式测试环境
✔动态测量-支持在运行时测量覆盖率
5.7.2.基本使用
1安装
bash
pip install coverage
2命令行使用
bash
#运行测试并收集覆盖率数据
coverage run-mpytesttests/
#生成终端报告
coverage report
#生成HTML报告
coverage html
3典型输出
text
Name Stmts MissCover
-----------------------------------
my_module.py 453 93%
tests/test.py 200 100%
-----------------------------------
TOTAL 65395%
5.7.3.高级功能
1配置文件(.coveragerc)
ini
[run]
source=my_package
omit=*/tests/*
branch=True
[report]
exclude_lines=
pragma:nocover
def__repr__
raise Not Implemented Error
2测量特定代码块
python
import coverage
cov=coverage.Coverage()
cov.start()
#被测代码
my_function()
cov.stop()
cov.save()
3合并多个运行结果
bash
coverage run--append-mpytesttests/unit/
coverage run--append-mpytesttests/integration/
coverage combine
coverage report
5.7.4.报告类型
5.7.5.集成方案
1与pytest集成
bash
#使用pytest-cov插件(推荐)
pytest --cov=my project tests
2与Django集成
python
settings.py
TEST_RUNNER='django.test.runner.DiscoverRunner'
3与CI系统集成
yaml
#GitHubActions示例
-name:Testwithcoverage
run:|
coverage run-mpytest
coverage xml
python-m codecov
5.7.6.最佳实践
设置合理目标:核心模块100%,非关键模块80%+
关注分支覆盖:使用--branch选项
定期检查趋势:监控覆盖率变化曲线
智能忽略代码:合理使用#pragma:nocover
结合其他工具:与pylint/flake8等配合使用
5.7.7.常见问题
Q1:如何测量特定子进程的覆盖率?
python
os.environ["COVERAGE_PROCESS_START"]=".coveragerc"
Q2:为什么某些行总是显示未覆盖?
可能是异常处理分支
使用coveragedebugsys检查测量系统
Q3:如何测量多线程程序的覆盖率?
python
cov=coverage.Coverage(data_suffix=True)
Coverage.py是Python开发者评估测试有效性的黄金标准工具,特别适合需要精确控制测试质量的项目。其丰富的功能和灵活的配置选项使其成为从小型脚本到企业级应用的首选覆盖率解决方案。
5.8Python在SonarQube和Jenkins的设置
1)在SonarQube建立Python项目,项目名为Python。
2)在SonarQube上建立Python项目,记住token
3)在Jenkins建立Credentials(同前)
4)在系统设置SonarQubeservers中建立Python
5.9一个例子
5.9.1 pipeline
groovy
pipeline {
agent any
environment {
JAVA_TOOL_OPTIONS = '-Dfile.encoding=UTF-8'
PYTHONIOENCODING = 'UTF-8'
PYTHON = 'python'
PROJECT_DIR = '.'
ALLURE_RESULTS = 'allure-results'
}
stages {
stage('Run Tests') {
steps {
script {
bat """
${
PYTHON} -m pytest -v --alluredir=${
ALLURE_RESULTS}
"""
}
}
}
stage('Run Coverage') {
steps {
script {
bat """
${
PYTHON} -m pytest --cov=./ --cov-report=html:site || echo "Coverage completed with warnings"
"""
}
}
}
stage('pylint Code Quality') {
steps {
script {
bat "${PYTHON} -m pylint --output-format=json *.py > report.json 2>&1 || exit 0"
bat "pylint-json2html -o report.html report.json"
}
}
}
stage('Pylint Score Check') {
steps {
script {
def score = bat(returnStdout: true, script: """
${
PYTHON} -m pylint --exit-zero *.py | findstr "Your code has been rated at"
""").trim()
echo "Pylint score: ${score}"
}
}
}
stage('SonarQube Analysis') {
steps {
script {
withSonarQubeEnv('Python') {
bat """
sonar-scanner -Dsonar.projectKey=Python \
-Dsonar.host.url=http://127.0.0.1:9000 \
-Dsonar.login=sqp_73a6cc32f8c3e1898cf32c2c1e576b6cc1864369\
-Dsonar.java.jdkHome=C:\\Tools\\jdk-17.0.15
"""
}
}
}
}
stage('SonarQube Quality Gate') {
steps {
script {
timeout(time:5,unit:'MINUTES') {
sleep(5)
def qg = waitForQualityGate()
if (qg.status != 'OK') {
echo "Status:${qg.status}"
error "Pipeline aborted due to quality gate failure:${qg.status}"
}
}
}
}
}
}
post {
always {
script {
allure([
includeProperties: false,
jdk: '',
properties: [],
reportBuildPolicy: 'ALWAYS',
results: [[path: "${ALLURE_RESULTS}"]]
])
}
script {
publishHTML (target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'site',
reportFiles: 'index.html',
reportName: 'Coverage Reports',
reportTitles: 'Coverage Report'])
}
script {
publishHTML (target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.\\',
reportFiles: 'report.html',
reportName: 'pylint Reports',
reportTitles: 'pylint Report'])
}
}
}
}
注意
1)命令行可以用
bach
pysonar --sonar-host-url=http://127.0.0.1:9000 --sonar-token=sqp_73a6cc32f8c3e1898cf32c2c1e576b6cc1864369 --sonar-project-key=Python
但是在pipeline中必须用Sonar-Scan
bach
sonar-scanner -Dsonar.projectKey=Python -Dsonar.host.url=http://127.0.0.1:9000 -Dsonar.login=sqp_73a6cc32f8c3e1898cf32c2c1e576b6cc1864369
sonar-scanner被测对象是64/32位,sonar-scanner也必须为64/32位
withSonarQubeEnv('Python')中的Python为5.2第5)步设置的项目名。
5.9.2总体图
5.9.3pytest-cov报告
5.9.4单元测试报告
5.9.5 Pylint报告
6 C++/C与Jenkins和SonarQube
6.1单元测试Googletest及在Jenkins中的应用
GoogleTest是Google开发的一个跨平台的C++测试框架,用于编写和运行单元测试。它是目前C++生态中最流行的测试框架之一,广泛应用于工业界和开源项目中。
6.1.1核心特性
1主要功能
✔丰富的断言机制-提供多种断言宏用于测试验证
✔测试发现机制-自动注册测试用例,无需手动维护测试列表
✔测试固件(TestFixtures)-支持测试环境的设置和清理
✔参数化测试-支持使用不同参数运行同一测试逻辑
✔死亡测试-专门测试程序崩溃或断言失败的情况
✔与构建系统集成-支持CMake、Bazel等主流构建系统
2断言类型
6.1.2.基本用法
1简单测试用例
cpp
#include
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=newint(42);
}
void TearDown() override{
//测试后清理
deletedata;
}
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 lib gtest-dev
#使用源码编译
git clone https://github.com/google/googletest.git
cdgoogletest
mk dir build&&cd build
cmake..&&make&&sudo make install
6.1.3.安装与集成
1安装方法
bash
#使用vcpkg
cpkg install gtest
#使用apt(Ubuntu)
sudo aptinstall 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(GTestREQUIRED)
add_executable(MyTeststest1.cpptest2.cpp)
target_link_libraries(MyTestsGTest::GTestGTest::Main)
6.1.4.高级特性
1死亡测试
cpp
TEST(DeathTest,InvalidPointer){
int*p=nullptr;
EXPECT_DEATH(*p=42,".*Segmentationfault.*");
}
2类型参数化测试
cpp
template
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;
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<<"Startingtest:"<<test_info.name()<<std::endl;
}
};
int main(intargc,char**argv){
::testing::InitGoogleTest(&argc,argv);
::testing::UnitTest::GetInstance()->listeners().Append(newMyListener);
returnRUN_ALL_TESTS();
}
6.1.5.最佳实践
测试命名规范:使用TestSuiteName_TestName_Test风格
优先使用EXPECT而非ASSERT,除非后续测试依赖前置条件
保持测试独立:测试之间不应有依赖关系
测试代码质量:测试代码应与产品代码保持相同质量标准
结合覆盖率工具:如gcov/lcov确保测试充分性
6.1.6.与其他工具对比
6.1.7.典型应用场景
✅单元测试驱动开发(TDD)
✅回归测试保障
✅API接口测试
✅算法正确性验证
✅跨平台兼容性测试
6.1.8与Jenkins集成
groovy
stages{
stage('gtest'){
steps{
echo"开始编译gtest"
sh'''
cd/home/jerry/googletest-main/googletest/myworkspace/process/nomal/\
&&g++../../src/TestAll.cppprocess.hprocess.cppprocessTest.cpp../../src/CallArgs.h../../src/CallArgs.cpp-oprocess-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:'MyTestReports',
reportTitles:'TheTestReport'])
}
6.2 Gcov介绍及在Jenkins中的应用
Gcov(GNUCoverage)是GCC(GNUCompilerCollection)自带的一个代码覆盖率分析工具,用于统计程序中哪些代码被执行过,哪些没有被执行。它常用于单元测试、软件测试优化和代码质量分析,帮助开发者发现未测试的代码路径。
6.2.1.Gcov的主要功能
语句覆盖率(StatementCoverage):统计每行代码是否被执行。
分支覆盖率(BranchCoverage):检查if-else、switch-case等分支是否被覆盖。
函数覆盖率(FunctionCoverage):统计哪些函数被调用过。
输出详细的覆盖率报告(.gcov文件),便于分析。
6.2.2.Gcov的基本使用流程
1编译代码时启用Gcov
使用-fprofile-arcs-ftest-coverage编译选项(GCC):
bash
gcc -f profile-arcs -f test-coverage -o my_programmy_program.c
.-f profile-arcs:记录代码执行路径。
-f test-coverage:生成.gcno文件(用于后续分析)。
2运行程序,生成.gcda文件
bash
./my_program
运行后,会生成.gcda文件(记录实际执行情况)。
3使用Gcov生成覆盖率报告
bash
gcov my_program.c
生成my_program.c.gcov文件,显示每行代码的执行次数。
示例输出:
text
-:10:printf("Hello,World!\n");
1:11:return0;
表示未执行,1表示执行了1次。
6.2.3.Gcov输出示例
1原始Gcov报告
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:intmain(){
1: 4: printf("Hello,Gcov!\n");
1: 5: return0;
-: 6:}
1:表示该行代码执行了1次。
-:表示未执行(如注释、空行等)。
2结合lcov生成HTML报告(更直观)
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
6.2.4.Gcov的典型应用场景
单元测试覆盖率检查
确保测试用例覆盖了所有关键代码路径。
回归测试优化
发现哪些代码在修改后未被测试。
代码质量分析
识别冗余或无效代码(从未执行的部分)。
6.2.5.Gcov的优缺点
1优点
免费且开源,集成在GCC中,无需额外安装。
轻量级,适合小型到中型项目。
支持C/C++(GCC编译的语言)。
2缺点
仅适用于GCC,不适用于Clang/MSVC。
报告较原始,需结合lcov/gcovr生成更好看的报告。
不能检测运行时错误(如内存泄漏),仅统计代码执行情况。
6.2.6.替代工具
6.2.7.总结
Gcov是GCC自带的代码覆盖率工具,适用于C/C++项目。
基本使用流程:
用-fprofile-arcs-ftest-coverage编译代码。
运行程序,生成.gcda文件。
用gcov生成报告,或lcov生成HTML可视化报告。
适用场景:单元测试、回归测试、代码优化。
如果你需要更强大的可视化,建议结合LCOV或gcovr使用!
6.2.8与jenkins集成
groovy
stage('gcov'){
steps{
echo"开始gcov"
sh'''
gcov-aprocess-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:'MyTestgCoverReports',
reportTitles:'TheTestgCoverReport'])
}
6.3 Lcov介绍及在Jenkins中的应用
Lcov是一个基于Gcov的代码覆盖率分析工具,用于生成更直观、可视化的覆盖率报告(如HTML)。它扩展了Gcov的功能,提供更友好的图形化界面,适用于C/C++项目的测试覆盖率分析。
6.3.1.Lcov的主要功能
支持多种覆盖率统计:
Ø行覆盖率(LineCoverage):代码行是否被执行。
Ø函数覆盖率(FunctionCoverage):函数是否被调用。
Ø分支覆盖率(BranchCoverage):条件分支(如if-else)是否被覆盖。
生成HTML报告,便于可视化分析。
支持过滤数据(如排除某些目录或文件)。
适用于大型项目,比Gcov更易于管理。
6.3.2Lcov的基本使用流程
1编译代码时启用Gcov
bash
gcc -f profile-arcs -f test-coverage -o my_programmy_program.c
-fprofile-arcs和-ftest-coverage用于生成覆盖率数据(.gcno文件)。
2运行程序,生成.gcda文件
bash
./my_program
运行后,会生成.gcda文件(记录实际执行情况)。
3使用Lcov收集覆盖率数据
bash
lcov -c -d.-o coverage.info
-c:捕获覆盖率数据。
-d.:从当前目录查找.gcda文件。
-ocoverage.info:输出到coverage.info文件。
4生成HTML报告
bash
genhtml coverage.info -o coverage_report
-ocoverage_report:生成HTML报告到coverage_report目录。
打开coverage_report/index.html查看报告:
https://ftp.tutorials24x7.com/uploads/1/1/1/0/111016327/lcov-report_orig.png
6.3.3.Lcov报告示例
1HTML报告内容
目录/文件列表:显示每个文件的覆盖率。
颜色标识:
Ø绿色:已覆盖的代码。
Ø红色:未覆盖的代码。
Ø黄色:部分覆盖(如分支未完全覆盖)。
详细数据:
Ø每行代码的执行次数。
Ø函数和分支的覆盖率统计。
2终端输出示例
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的高级用法
1排除特定文件/目录
bash
lcov --remove coverage.info "/usr/include/*""*/test/*" -o filtered.info
--remove用于排除不需要分析的文件(如系统头文件或测试代码)。
2合并多个覆盖率数据
bash
lcov -a coverage1.info -a coverage2.info -o combined.info
-a用于合并多个测试运行的覆盖率数据。
3生成JSON或其他格式
bash
lcov –json -o coverage.json
可用于集成到CI/CD工具(如Jenkins、GitLabCI)。
6.3.5.Lcov的优缺点
1优点
可视化强:HTML报告比Gcov的文本报告更直观。
支持多种覆盖率统计(行、函数、分支)。
适用于大型项目,可过滤无关文件。
开源免费,与Gcov无缝集成。
2缺点
依赖Gcov,仅适用于GCC编译的代码(不适用于Clang/MSVC)。
HTML报告较大,可能不适合超大型项目。
需要额外安装(部分Linux发行版默认不包含)。
6.3.6.Lcov的替代工具
6.3.7.总结
Lcov是Gcov的增强版,提供HTML可视化报告。
典型使用流程:
Ø用-fprofile-arcs-ftest-coverage编译代码。
Ø运行程序,生成.gcda文件。
Ø用lcov收集数据,genhtml生成报告。
适用场景:
ØC/C++项目的单元测试覆盖率分析。
Ø持续集成(CI)中的代码质量检查。
推荐使用场景:
如果你在用GCC+Gcov,并需要更好的可视化,Lcov是最佳选择!
如果项目较小,可以试试gcovr(更轻量)。
6.3.8与jenkins集成
groovy
stage('lcov'){
steps{
echo"开始lcov"
sh'''
lcov-c-d.-ocoverage.info\
&&genhtmlcoverage.info-ocoverage_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:'MyTestlCoverReports',
reportTitles:'TheTestlCoverReport'])
}
6.4 CPPcheck介绍及在Jenkins中的应用
6.4.1工具概述
Cppcheck是一个开源的静态代码分析工具,专门用于检测C/C++代码中的错误。它不依赖编译器,而是直接分析源代码,能发现许多编译器无法检测的问题(如内存泄漏、逻辑错误等)。
1主要特点
✔轻量级:比ClangStaticAnalyzer等工具更快
✔低误报率:相比部分静态分析工具更准确
✔跨平台:支持Windows/Linux/macOS
✔可扩展:支持自定义规则
✔支持多种标准:C89/C99/C11/C++03/C++11/C++14/C++17
6.4.2.主要检测能力
6.4.3.基本使用方法
1安装方法
bash
#Linux(Debian/Ubuntu)
sudo apt install cppcheck
#macOS
brew install cppcheck
#Windows
choco install cppcheck
#或从官网下载二进制包
2基本扫描命令
bash
#检查单个文件
cpp check example.cpp
#递归检查整个项目
cpp check --enable=all ./src/
#输出到文件
cpp check --output-file=report.txt main.cpp
3常用选项
6.4.4.高级功能
1生成XML报告
bash
cppcheck --xml-version=2 --xml.2>report.xml
2与CMake集成
cmake
find_program(CPPCHECKcppcheck)
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
Checkingexample.cpp...
[example.cpp:12]:(error)Memoryleak:ptr
[example.cpp:25]:(warning)Possiblenullpointerdereference:p
[example.cpp:42]:(style)Variable'i'isnotused
6.4.6.同类工具对比
6.4.7.最佳实践建议
集成到CI/CD:在代码提交/合并时自动运行
渐进式启用:先启用基本检查,逐步增加规则
排除第三方代码:--suppress=:external/
定期更新:新版会持续改进检测能力
结合动态分析:与Valgrind/ASAN等工具互补使用
6.4.8.总结
Cppcheck是C/C++开发者工具箱中不可或缺的静态分析工具,特别适合:
需要快速反馈的中小型项目
资源受限的嵌入式开发环境
作为编译警告之外的额外质量保障
推荐使用场景:
✅每日开发中的快速检查
✅CI流水线中的基础质量门禁
✅遗留代码的质量评估
6.4.9 CPPcheck与jenkins集成
groovy
stage('cppcheck'){
steps{
echo"开始cppcheck"
sh'''
cppcheck--enable=all--output-file=report.htmlprocess.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:'CppcheckReports',
reportTitles:'CppcheckReport'])
}
6.5一个例子
6.5.1 pipeline
groovy
pipeline{
agentany
stages{
stage('gtest'){
steps{
echo"开始编译gtest"
sh'''
cd/home/jerry/googletest-main/googletest/myworkspace/process/nomal/\
&&g++../../src/TestAll.cppprocess.hprocess.cppprocessTest.cpp../../src/CallArgs.h../../src/CallArgs.cpp-oprocess-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-aprocess-process\
&&gcovr-r.--html-details-o./html/result.html
'''
echo"结束gcov"
}
}
stage('lcov'){
steps{
echo"开始lcov"
sh'''
lcov-c-d.-ocoverage.info\
&&genhtmlcoverage.info-ocoverage_report
'''
echo"结束lcov"
}
}
stage('cppcheck'){
steps{
echo"开始cppcheck"
sh'''
cppcheck--enable=all--output-file=report.htmlprocess.cpp
'''
echo"结束cppcheck"
}
}
stage('CodeAnalysis'){
steps{
echo"开始分析"
withSonarQubeEnv('SonarQube'){
sh"cd/home/jerry/googletest-main/googletest/myworkspace/process/nomal/&&sonar-scanner"
}
echo"结束分析"
}
}
stage('QualityGate'){
steps{
echo"开始质量分析"
script{
timeout(time:10,unit:'MINUTES'){
sleep(5)
try{
defqg=waitForQualityGate()
echo"Status:${qg.status}"
if(qg.status!='OK'){
echo"Status:${qg.status}"
error"Pipelineabortedduetoqualitygatefailure:${qg.status}"
}
}
catch(exc){
echo"waitForQualityGate()iserror"
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:'MyTestReports',
reportTitles:'TheTestReport'])
}
script{
publishHTML(target:[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'/home/jerry/googletest-main/googletest/myworkspace/process/nomal/',
reportFiles:'report.html',
reportName:'CppcheckReports',
reportTitles:'CppcheckReport'])
}
script{
publishHTML(target:[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'/home/jerry/googletest-main/googletest/myworkspace/process/nomal/html/',
reportFiles:'result.html',
reportName:'MyTestgCoverReports',
reportTitles:'TheTestgCoverReport'])
}
script{
publishHTML(target:[
allowMissing:false,
alwaysLinkToLastBuild:true,
keepAll:true,
reportDir:'/home/jerry/googletest-main/googletest/myworkspace/process/nomal/coverage_report/',
reportFiles:'index.html',
reportName:'MyTestlCoverReports',
reportTitles:'TheTestlCoverReport'])
}
}
}
6.5.2总报告
6.5.3单元测试报告
6.5.4Lcov报告
6.5.5Gcov报告
6.5.6Cppcheck报告