Jenkins从入门到精通——代码质量
代码质量
1、静态代码分析
静态代码分析是指在不运行程序的前提下,对源代码进行分析或检查,范围包括代码风格、可能出现的空指针、代码块大小、重复的代码等。
没有通过编译,静态代码分析就没有意义。所以在整个 pipeline 中,静态代码分析通常被安排在编译阶段之后。
1.1.1 代码规范检查
作为一个苦逼的程序员,因为种种原因经常需要阅读别人写的代码。
您是否有过因代码杂乱冗余而心生厌恶,您是否有过因代码晦涩难懂而抓狂,您是否有过因代码低级的逻辑错误而愤概,您是否有过因代码结构不合常规而需要到处查找,您是否有过因看到几百甚至上千行代码的方法而望洋兴叹,您是否有过因代码缺少注释而猜测以及花很多时间去理清楚前后逻辑。
请遵循一些简单的规范,写干净一致的代码。
2017年阿里云巴巴发布了《阿里巴巴 Java 开发手册》(https://developer.aliyun.com/special/tech-java),该开发手册是阿里巴巴集团技术团队的集体经验总结,经历了多次大规模一线实战的检验及不断完善。现代软件行业的高速发展对于开发者的综合素质要求越来越高,因为不仅是编程知识点,其它维度的知识点也会影响到软件的最终交付质量。比如:数据库的表结构和索引设计缺陷可能带来软件上的架构缺陷或性能风险;工程结构混乱导致维护困难;没有鉴权的漏洞代码被黑客攻击等等。所以本手册以 Java 开发者为中心视角,划分为编程规约、异常日志规约、MySQL规约、工程规约、安全规约五大块,再根据内容特征,细分成若干二级子目录。根据约束力强弱及故障敏感性,规约依次分为强制、推荐、参考三大类。对于规约里的内容,“说明”对内容做了引申和解释;“正例”提倡什么样的编码和实现方式;“反例”说明需要提防的雷区,以及真实的错误案例。
有了阿里巴巴的 "光环",公司内技术人就 "代码规范" 达成共识就变得容易了。至于《阿里巴巴 Java 开发手册》里的规范是否是最好的,这相对来说就是次要的一个问题了。
但是如何实施?安排一个人定期 review 团队成员的代码是否符合规范?
1.1.2 使用 PMD 进行代码规范检查
解决这个问题的正确思路是让机器来对规范的实施检查。接下来我们讨论具体实施步骤。
PMD (https://pmd.github.io) 是一款可扩展的静态代码分析器,它不仅可以对代码风格进行检查,还可以检查设计、多线程、性能等方面的问题。
PMD 官方介绍:
PMD is a source code analyzer. It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth. It supports Java, JavaScript, Salesforce.com Apex and Visualforce, PLSQL, Apache Velocity, XML, XSL.
Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code in Java, C, C++, C#, Groovy, PHP, Ruby, Fortran, JavaScript, PLSQL, Apache Velocity, Scala, Objective C, Matlab, Python, Go, Swift and Salesforce.com Apex and Visualforce.
Maven 的 PDM 插件(https://pmd.github.io) 使我们能在 Maven 上使用 PDM。
使用步骤如下:
(1) 在 Maven 项目的 pom.xml 中加入 PMD 插件,具体用法见 http://maven.apache.org/plugins/maven-pmd-plugin。
(2.1) quick start 安装方法见 https://pmd.github.io
cd $HOME wget https://github.com/pmd/pmd/releases/download/pmd_releases%2F6.35.0/pmd-bin-6.35.0.zip unzip pmd-bin-6.35.0.zip alias pmd="$HOME/pmd-bin-6.35.0/bin/run.sh pmd" pmd -d /usr/src -R rulesets/java/quickstart.xml -f text
(2.2) 安装 jenkins PMD 插件作用是将 PMD 报告呈现在任务详情页中,安装方法见 https://github.com/jenkinsci/pmd-plugin。
(3) 在 Jenkinsfile 中加入 pmd 步骤。
pipeline{ agent any tools{ maven 'maven-3.6.1' } stages{ stage('pmd'){ steps { bat "mvn pmd:pmd" bat "mvn pmd:pmd" } } } post{ always{ pmd(canRunOnFailed:true,pattern:'**/target/pmd.xml') } } }
(4) 执行完成后,可以在任务详情页看到 PMD 报告的链接。
1.1.3 各静态代码分析器之间的区别
目前每种语言基本上都有自己的静态代码分析器,比如 Java 语言,除 PMD 外,还有 Check-style、FindBugs 等。但是没有一款能 "大一统",实现对所有语言、所有场景的支持。
另外,同一种语言下的不同分析器,它们在功能上既有区别,又有重叠,需要根据自己团队的情况进行选择。但是不论选择哪款分析器,所有进行静态代码分析的地方都必须统一分析规则。比如决定使用阿里巴巴的开发规范,那么 Maven 插件、IDE 插件以及后面说到的 SonarQube 都必须使用;否则,分析结果可能会不一致,进而影响分析结果的可信度。
2、单元测试
每种编程语言都有自己的单元测试框架。执行单元测试的工作一般由构建工具来完成。Jenkins 做的只不过是执行这些构建工具的单元测试命令,然后对测试报告进行收集,并呈现。单元测试还是要人来写,jenkins 自动化测试并不是指的 Jenkins 来代替人自动写测试。
2.1 JUnit 单元测试报告
JUnit是一个Java语言的单元测试框架。它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中为最成功的一个。JUnit有它自己的JUnit扩展生态圈。多数Java的开发环境都已经集成了JUnit作为单元测试的工具。
JUnit Plugin 官方介绍:
The JUnit plugin provides a publisher that consumes XML test reports generated during the builds and provides some graphical visualization of the historical test results (see JUnit graph for a sample) as well as a web UI for viewing test reports, tracking failures, and so on. Jenkins understands the JUnit test report XML format (which is also used by TestNG). When this option is configured, Jenkins can provide useful information about test results, such as trends.
The plugin also provides a generic API for other unit-test publisher plugins in Jenkins. This functionality was part of the Jenkins Core until it was split out to this plugin in version in 1.577.
当执行 maven test 命令时,Maven 会执行测试阶段(包括单元测试),然后生成测试报告。
使用步骤如下:
(1) 安装 Jenkins JUnit 插件(https://plugins.jenkins.io/junit)。
(2) 在 Jenkins 中加入 junit 步骤。通常将 junit 步骤放在 post always 中,因为当前测试不通过时,我们依然可以收集到测试报告。
stage('junit'){ steps { bat "mvn test" } } post{ always{ junit testResults: "**/target/surefire-reports/*.xml" } }
2.2 JaCoCo 实现代码覆盖率
JUnit 只是方便我们写单元测试的一个框架,但是并没有告诉我们有多少代码被测试覆盖到了。而 JaCoCo 填补了这一空白。JaCoCo 是一个免费的 Java 代码覆盖率的库,能帮助我们检测出代码覆盖率,并输出覆盖率报告。
JaCoCo 提供了以下几个纬度的覆盖率分析。
- 指令覆盖
- 分支覆盖
- 代码行覆盖
- 方法覆盖
- 类覆盖
- 行覆盖率
- 全复杂度
使用步骤如下:
(1) 安装 JaCoCo 插件(https://plugins.jenkins.io/jacoco)。
(2) 在 Maven 项目中引入 JaCoCo 插件,执行 maven jacoco 生成代码覆盖率报告。
pom.xml中增加依赖:
<plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.7.7.201606060606</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <phase>prepare-package</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin>
Jenkinsfile 中增加以下脚本:
stage('Test') {
steps {
sh 'mvn clean test'
}
}
stage('Generate Test Report') {
steps {
script {
try {
sh 'mvn org.jacoco:jacoco-maven-plugin:report'
} finally {
junit 'target/surefire-reports/*.xml'
}
}
}
}
stage('Publish Test Coverage Report') {
steps {
step([$class: 'JacocoPublisher',
execPattern: 'target/*.exec',
classPattern: 'target/classes',
sourcePattern: 'src/main/java',
exclusionPattern: 'src/test*'
])
}
}
假如对用户影响很大的bug在5%的代码中,2个不同的测试策略,1个测试策略的代码覆盖率只有70%但覆盖到了这5%的代码,另1个测试策略的代码覆盖率有90%但未覆盖到这5%的代码。显然,从对用户的影响来看,70%的测试质量比90%的测试质量更高。
现在我们对代码覆盖率应该有一种更进一步的认识:除非100%覆盖否则不一定高覆盖率就一定比低覆盖率的测试质量更高,而决定测试质量的关键在于测试策略(测试宽度和测试深度组合的设计)。好的测试策略会帮助你用更少的时间尽早发现对用户影响最大的一批bug,而不是花费较多的时间去覆盖更高的代码覆盖率,这会影响我们测试进度和测试资源。
如何设计好的测试策略来实现高性价比的代码覆盖率,也是我们需要去研究的课题。Google代码覆盖率最佳实践: https://testing.googleblog.com/2020/08/code-coverage-best-practices.html