代码质量检测(jacoco & pmd & pipeline)

代码质量检测

一、静态代码分析

静态代码分析是指在不运行程序的前提下,对源代码进行分析或检查,范围包括代码风格、可能出现的空指针、代码块大小、重复的代码等。

  1. pipeline中,静态代码分析通常被安排在编译阶段之后
  2. 代码规范检查的方案是使用构建工具或代码分析器进行代码规范检查,如果不通过,pipeline就中止
1.1 静态代码分析工具
1. PMD

可扩展的静态代码分析器,不仅可以对代码风格进行检查,还可以检查设计、多线程、性能等方面的问题

  • 1) 在maven项目的pom.xml中加入PMD插件

    <!-- 编码规约扫描命令: mvn pmd:pmd-->
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-pmd-plugin</artifactId>
        <version>3.8</version>
        <configuration>
            <rulesets>
                <ruleset>rulesets/java/ali-comment.xml</ruleset>
                <ruleset>rulesets/java/ali-concurrent.xml</ruleset>
                <ruleset>rulesets/java/ali-constant.xml</ruleset>
                <ruleset>rulesets/java/ali-exception.xml</ruleset>
                <ruleset>rulesets/java/ali-flowcontrol.xml</ruleset>
                <ruleset>rulesets/java/ali-naming.xml</ruleset>
                <ruleset>rulesets/java/ali-oop.xml</ruleset>
                <ruleset>rulesets/java/ali-orm.xml</ruleset>
                <ruleset>rulesets/java/ali-other.xml</ruleset>
                <ruleset>rulesets/java/ali-set.xml</ruleset>
            </rulesets>
            <printFailingErrors>true</printFailingErrors>
        </configuration>
        <executions>
            <!-- 绑定pmd:check到verify生命周期 -->
            <execution>
                <id>pmd-check-verify</id>
                <phase>verify</phase>
                <goals>
                    <goal>check</goal>
                </goals>
            </execution>
            <!-- 绑定pmd:pmd到site生命周期 -->
            <execution>
                <id>pmd-pmd-site</id>
                <phase>site</phase>
                <goals>
                    <goal>pmd</goal>
                </goals>
            </execution>
        </executions>
        <!-- p3c依赖 -->
        <dependencies>
            <dependency>
                <groupId>com.alibaba.p3c</groupId>
                <artifactId>p3c-pmd</artifactId>
                <version>1.3.6</version>
            </dependency>
        </dependencies>
    </plugin>
    
      1. 安装Jenkins PMD插件
      https://pmd.github.io/
      
      1. Jenkinsfile中加入pmd
      pipeline {
          agent any
          
          tools {
              maven 'mvn-3.5.4'
          }
          stages {
              stage('pmd') {
                  steps {
                      sh "mvn pmd:pmd"
                  }
              }
          }
          post {
              always {
                  pmd(canRunOnFailed: true, pattern: '**/target/pmd.xml')
              }
          } 
      }
      
      1. 在任务详情页查看PMD报告连接

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W3mDWncg-1591925801000)(img\PMD.png)]

      • 详细页面

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p0RGoY1H-1591925801002)(img\PMD_DETAIL.png)]

1.2. 各静态代码分析器之间的区别

目前每种语言基本上都有自己的静态代码分析器,比如Java语言,除PMD外,还有Check-style、FindBugs等。但是没有一款能“大一统”,实现对所有语言、所有场景的支持。

Java静态分析工具分析对象应用技术原理描述内置编码规范
CheckStyleJava源代码缺陷模式匹配是 SourceForge 的开源项目,通过检查对代码编码格式,命名约定,Javadoc,类设计等方面进行代码规范和风格的检查Javadoc 注释:检查类及方法的 Javadoc 注释
命名约定:检查命名是否符合命名规范
标题:检查文件是否以某些行开头
Import 语句:检查 Import 语句是否符合定义规范
代码块大小,即检查类、方法等代码块的行数
空白:检查空白符,如 tab,回车符等
修饰符:修饰符号的检查,如修饰符的定义顺序
块:检查是否有空块或无效块
代码问题:检查重复代码,条件判断,模数等问题
类设计:检查类的定义是否符合规范,如构造函数的定义等问题
FindBugs字节码缺陷模式匹配;数据流分析FindBugs 通过检查类文件或 JAR 文件,将字节码与一组缺陷模式进行对比从而发现代码缺陷,完成静态代码分析。Bad practice 坏的实践:常见代码错误,用于静态代码检查时进行缺陷模式匹配
Correctness 可能导致错误的代码,如空指针引用等
国际化相关问题:如错误的字符串转换
可能受到的恶意攻击,如访问权限修饰符的定义等
多线程的正确性:如多线程编程时常见的同步,线程调度问题。
运行时性能问题:如由于变量定义,方法调用导致的代码低效问题。
PMDJava源代码缺陷模式匹配PMD 通过其内置的编码规则对 Java 代码进行静态检查,主要包括对潜在的 bug,未使用的代码,重复的代码,循环体创建新对象等问题的检验。可能的 Bugs:检查潜在代码错误,如空 try/catch/finally/switch 语句
未使用代码(Dead code):检查未使用的变量,参数,方法
复杂的表达式:检查不必要的 if 语句,可被 while 替代的 for 循环
重复的代码:检查重复的代码
循环体创建新对象:检查在循环体内实例化新对象
资源关闭:检查 Connect,Result,Statement 等资源使用之后是否被关闭掉
JtestJava源代码缺陷模式匹配;数据流分析Jtest 的静态代码分析功能能够按照其内置的超过 800 条的 Java 编码规范自动检查并纠正这些隐蔽且难以修复的编码错误。可能的错误:如内存破坏、内存泄露、指针错误、库错误、逻辑错误和算法错误等
未使用代码:检查未使用的变量,参数,方法
初始化错误:内存分配错误、变量初始化错误、变量定义冲突
命名约定:检查命名是否符合命名规范
Javadoc 注释:检查类及方法的 Javadoc 注释
线程和同步:检验多线程编程时常见的同步,线程调度问题
国际化问题:
垃圾回收:检查变量及 JDBC 资源是否存在内存泄露隐患

二、单元测试

2.1 JUnit单元测试

JUnit是一个Java语言的单元测试框架,由Kent Beck和Erich Gamma创建。当执行maven test命令时,Maven会执行测试阶段(包括单元测试),然后生成测试报告。

2.2 收集并展示JUnit测试报告的步骤
  • 1)安装Jenkins JUnit插件

  • 2)在Jenkins中加入junit步骤。通常将junit步骤放在post always中,因为当测试不通过时,我们依然可以收集到测试报告。写法如下:

    post {
    	always{
    		junit testResults: "**/target/surefire-reports/*.xml"
    	}
    }
    
    • 注:**/target/surefire-reports/*.xml表示只要是target/surefire-reports目录下的XML文件就会被当作JUnit测试报告处理,而不论target在哪个层级的目录下
2.3 查看测试报告
  • 链接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jUm2vAQr-1591925801004)(img\test_junit.png)]

  • 详情页

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Li2YFTgT-1591925801006)(img\test_junit_detail.png)]

三、JaCoCo实现代码覆盖率

3.1 JaCoCo简介
  • JUnit只是编写单元测试的一个框架,并没有涉及到代码覆盖率,而JaCoCo提供了该功能;

  • JaCoCo是一个免费的Java代码覆盖率的库,能帮助检测出代码覆盖率,并输出覆盖率报告

  • JaCoCo提供了以下几个维度的覆盖率分析

    • 指令覆盖率(Instruction Coverage)

      Jacoco计数的最小单元是Java字节码指令,它为执行/未执行代码提供了大量的信息。这个指标完全独立于源格式,在类文件中缺少debug信息时也可以使用。

    • 分支覆盖率(Branch Coverage)

      Jacoco为if和switch语句计算分支覆盖率。这个指标计算一个方法中的分支总数,并决定已执行和未执行的分支的数量。分支覆盖率在class文件中缺少debug信息时也可使用。异常处理不在分支覆盖的统计范围内。

      • 全部未覆盖:所有分支均未执行,红色标志

      • 部分覆盖:只有部分分支被执行,黄色标志

      • 全覆盖:所有分支均已执行,绿色标志

    • 圈复杂度覆盖率(Cyclomatic Complexity Coverage)

      Jacoco对每个非抽象方法计算圈复杂度,总结类、包、组的复杂性。
      圈复杂度:在(线性)组合中,计算在一个方法里面所有可能路径的最小数目。所以复杂度可以作为度量单元测试是否有完全覆盖所有场景的一个依据。在没有debug信息的时候也可以使用。

      • 圈复杂度V(G)是基于方法的控制流图的有向图表示:V(G) = E - N + 2
      • E是边界数量,N是节点数量。
      • Jacoco基于下面方程来计算复杂度,B是分支数量,D是决策点数量:
        • V(G) = B - D + 1
      • 基于每个分支的被覆盖情况,Jacoco也未每个方法计算覆盖和缺失的复杂度。缺失复杂度同样表示测试案例没有完全覆盖到这个模块。注意Jacoco不将异常处理作为分支,try/catch块也同样不增加复杂度。
    • 行覆盖率(Line Coverage)

      所有类文件均携带debug信息编译,则每行的覆盖率可计算。当至少一个指令被指定到源码行且已执行时,该源码行被认为已执行。

      • 全部未覆盖:该行中指令均未执行,红色标志

      • 部分覆盖:该行中部分指令执行,黄色标志

      • 全覆盖:该行中所有指令已执行,绿色标志

    • 方法覆盖率(Method Coverage)

      每个非抽象方法至少包含一个指令。当至少一个指令被执行,该方法被认为已执行。由于Jacoco基于字节码级别的,构造函数和静态初始化方法也被当作方法计算。其中有些方法,可能无法直接对应到源码中,比如默认构造器或常量的初始化命令。

    • 类覆盖率(Class Coverage)

      当类中至少有一个方法已执行,则该类被认为已执行。Jacoco中认为构造函数和静态初始化方法也当作被执行过的方法。Java接口类型若包含静态初始化方法,这种接口也被认为是可执行的类。

3.2 JaCoCo插件的使用步骤
  • 1)安装JaCoCo插件(https://plugins.jenkins.io/jacoco)

  • 2)在Maven项目中引入JaCoCo插件,执行mavenjacoco生成代码覆盖率报告

    <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.8.5</version>
        <configuration>
           <systemPropertyVariables>
            	<jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
            </systemPropertyVariables>
        </configuration>
        <executions>
            <execution>
                <id>pre-test</id>
                <goals>
                    <goal>prepare-agent</goal>
                </goals>
            </execution>
            <execution>
                <id>post-test</id>
                <phase>test</phase>
                <goals>
                    <goal>report</goal>
                </goals>
            </execution>
            <execution>
                <id>post-unit-test</id>
                <phase>test</phase>
                <goals>
                    <goal>report</goal>
                </goals>
                <configuration>
                	<!-- jacoco执行数据的文件路径 -->
                    <dataFile>target/jacoco.exec</dataFile>
                    <!-- 输出报告的路径 -->
                    <outputDirectory>target/jacoco-ut</outputDirectory>
                </configuration>
            </execution>
        </executions>
    </plugin>
    
      1. 使用jacoco步骤,jacoco步骤在mvn命令之后执行
    steps{
    	sh "mvn clean install"
    	jacoco {
    		// 代码覆盖率统计文件位置. Ant风格路径表达式
    		execPattern: 'target/**/*.exec',
    		// classes文件位置. Ant风格路径表达式
    		classPattern: 'target/classes',
    		// 源代文件位置. Ant风格路径表达式
    		sourcePattern: 'src/main/java',
    		// 排除分析的位置. Ant风格路径表达式
    		exclusionPattern: `src/test`,
    		// 是否禁用每行覆盖率的原文件显示
    		skipCopyOfSrcFiles: false,
    		// 如果为true,则对各维度的覆盖率进行比较。如果任何一个维度的当前覆盖率小于最小覆盖率阈值,则构建状态为失败:如果当前覆盖率在最大阈值和最小阈值之间,则当前构建状态为不稳定; 如果当前覆盖率大于最大阈值,则构建成功
    		changeBuildStatus: true,
    		// 字节码执行覆盖率
    		minimumInstructionCoverage: '30', maximumInstructionCoverage: '70',
    		// 行覆盖率
    		minimumLineCoverage: '30',maximumLineCoverage: '70',
    		// 圈复杂度覆盖率
    		minimumComplexityCoverage: '30',minimumComplexityCoverage: '70',
    		// 方法覆盖率
    		minimumMethodCoverage: '30',maximumMethodCoverage: '70',
    		// 类覆盖率
    		minimumClassCoverage: '30',maximumClassCoverage: '70',
    		//分支覆盖率
    		minimumBranchCoverage: '30',maximumBranchCoverage: '70',
    		// 如果为true,则只有所有维度的覆盖率变化量的绝对值小于相应的变化量阈值时,构建结果才为成功
    		buildOverBuild: true
    		// 以下是各个维度覆盖率的变化量阈值
    		deltaInstructionCoverage: '80', deltaLinenCoverage: '80',
    		deltaMethodCoverage: '80', deltaClassCoverage: '80',
    		deltaComplexityCoverage: '80', deltaBranchCoverage: '80'	
    	}
    }
    
      1. pipeline运行完成后,我们可以在任务详情页的下方看到报告

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OGxgjIvW-1591925801008)(img\jacoco_pipeline_detail.png)]

  • 代码覆盖率越高,软件的质量就越高吗?我们来看看《软件之道:软件开发争议问题剖析》中是怎么说的:

    • 为了找到两者之间的关系,把Windows Vista(4000多万行代码、几千个二进制文件、数千名工程师)的分支覆盖率(Branch Coverage)与块覆盖率(Block Coverage)和其发布六个月内的现场缺陷对应起来。我们观察到覆盖率和质量之间有弱正相关性,预测查准率和查全率较差(查准率为83.8%,查全率为54.8%)
    • 代码覆盖率最好不要单独使用,而是需要与其他指标,如代码变动率、复杂度等一并考虑

更多内容请关注公众号:
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值