【Jenkins使用之九】jenkins自动测试

环境
  CentOS Linux release 7.6.1810
  jdk1.8.0_65
  apache-tomcat-8.5.45
  Jenkins-2.235.5
  apache-maven-3.6.3
  git-2.9.5
  gradle-6.6.1
  SonarQube-6.7.7(LTS)
  sonar-scanner-cli-4.2.0.1873
拓扑:
  node1:安装GitLab、SonarQube
  node2:安装Jenkins、Git、MAVEN

我们开发的软件都需要测试,才能上线,软件测试按阶段划分:
(1)单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。
(2)集成测试(integration testing),是单元测试的下一阶段,是指将通过测试的单元模块组装成系统或子系统,再进行测试,重点测试不同模块的接口部门。
(3)系统测试(system testing),指的是将整个软件系统看做一个整体进行测试,包括对功能、性能,以及软件所运行的软硬件环境进行测试。
(4)验收测试(acceptance testing),指的是在系统测试的后期,以用户测试为主,或有测试人员等质量保障人员共同参与的测试,它也是软件正式交给用户使用的最后一道工序。

一般性能测试:指的是让被测系统在正常的软硬件环境下运行,不向其施加任何压力的性能测试。
稳定性测试也叫可靠性测试(reliability testing):是指连续运行被测系统检查系统运行时的稳定程度。
负载测试(load testing):是指让被测系统在其能忍受的压力的极限范围之内连续运行,来测试系统的稳定性。
压力测试(stress testing):是指持续不断的给被测系统增加压力,直到将被测系统压垮为止,用来测试系统所能承受的最大压力。

一、单元测试
每种编程语言都有单元测试框架,比如java语言中JUnit,执行单元测试的工作一般由自动化构建工具完成,但是构建工具只是执行了单元测试的命令,将单元测试调用起来,构建工具不会帮我们创建单元测试用例,单元测试都是开发事先写好的。

1、Junit单元测试报告
这里演示一个jenkins使用JUnit插件收集展示单元测试报告的示例:
(1)Jenkins安装JUnit插件
(2)创建一个MultiBranch pipeline 工程
maven项目pom.xml中引入maven-surefire-plugin插件

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <!--表示执行任何子目录下所有命名以Tests结尾的java类 -->
        <includes>
            <include>**/*Tests.java</include>
        </includes>
        <!--表示不执行任何子目录下所有命名以Test开头的java类 -->
        <!--
        <excludes>
            <exclude>**/Test*.java</exclude>
        </excludes>
        -->
    </configuration>
</plugin>

完整pom示例:

<?xml version="1.0" encoding="UTF-8"?>
<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>com.macro.mall</groupId>
    <artifactId>mall-tiny</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>mall-tiny</name>
    <description>mall-tiny project</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <skipTests>false</skipTests>
        <!--该方法有漏洞,容易被黑客远程放入挖矿机镜像,开启需做好防范,
        年少无知的我,开放2375,没做ip限制,被拉去挖矿了。。。
        推荐使用CA加密端口-->
        <docker.host>http://192.168.118.106:2375</docker.host>
        <docker.maven.plugin.version>1.2.2</docker.maven.plugin.version>
        <druid.version>1.1.10</druid.version>
        <hutool.version>4.5.7</hutool.version>
        <swagger2.version>2.9.2</swagger2.version>
        <swagger-models.version>1.6.0</swagger-models.version>
        <swagger-annotations.version>1.6.0</swagger-annotations.version>
        <mysql-connector.version>8.0.16</mysql-connector.version>
        <jjwt.version>0.9.0</jjwt.version>
        <mybatis-plus.version>3.3.2</mybatis-plus.version>
        <velocity.version>2.2</velocity.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <dependencies>
        <!--SpringBoot通用依赖模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--SpringBoot校验框架-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <!--集成druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <!--Mysql数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector.version}</version>
        </dependency>
        <!--Swagger-UI API文档生产工具-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger2.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger2.version}</version>
        </dependency>
        <!--解决Swagger 2.9.2版本NumberFormatException-->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>${swagger-models.version}</version>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>${swagger-annotations.version}</version>
        </dependency>
        <!--redis依赖配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--SpringSecurity依赖配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--Hutool Java工具包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
        <!--JWT(Json Web Token)登录支持-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jjwt.version}</version>
        </dependency>
        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--SpringBoot配置处理-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!--MyBatis Plus 依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!--MyBatis Plus 代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!--Velocity模板引擎-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>${velocity.version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <!--表示执行任何子目录下所有命名以Tests结尾的java类 -->
                    <includes>
                        <include>**/*Tests.java</include>
                    </includes>
                    <!--表示不执行任何子目录下所有命名以Test开头的java类 -->
                    <!--
                    <excludes>
                        <exclude>**/Test*.java</exclude>
                    </excludes>
                    -->
                </configuration>
            </plugin>
            <!-- docker-maven-plugin 插件就是为了帮助我们在Maven工程中,通过简单的配置,自动生成镜像并推送到仓库中。
                微服务部署有两种方法:
                (1)手动部署:首先基于源码打包生成jar包(或war包),写个Dockerfile文件,基于基础镜像搞个新镜像,将jar包(或war包)上传至虚拟机并拷贝至JDK容器。(太麻烦了)
                (2)通过Maven插件自动部署。 这也是企业实际开发中经常使用的方法。
                参考:
                https://blog.csdn.net/weixin_44424668/article/details/104062822
                https://www.cnblogs.com/jpfss/p/10945324.html
             -->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>${docker.maven.plugin.version}</version>
                <executions>
                    <execution>
                        <id>build-image</id>
                        <phase>package</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!--修改imageName节点的内容,改为私有仓库地址和端口,再加上镜像id和TAG,我们要直接传到私服-->
                    <!--配置最后生成的镜像名,docker images里的,我们这边取项目名:版本-->
                    <imageName>192.168.82.46:18083/${project.artifactId}:${project.version}</imageName>
                    <dockerHost>${docker.host}</dockerHost>
                    <!--基础镜像,相当于Dockerfile里的from-->
                    <baseImage>ascdc/jdk8</baseImage>
                    <!--入口点,project.build.finalName就是project标签下的build标签下的filename标签内容,testDocker-->
                    <!--相当于启动容器后,会自动执行java-jar/testDocker.jar-->
                    <entryPoint>["java", "-jar","/${project.build.finalName}.jar"]</entryPoint>
                    <!--是否推送到docker私有仓库  设置为true 每次构建都要推送镜像-->
                    <!--<pushImage>true</pushImage>-->
                    <!-- 要在$MAVEN_HOME/settings.xml中配置对应的serverId才行 否咋报错Fail : no basic auth credentials -->
                    <serverId>cicd-docker-release</serverId>
                    <registryUrl>192.168.82.46:18083</registryUrl>
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <!--
                                把哪个文件上传到docker,相当于Dockerfile里的add testDocker.jar
                                mvn clean,mvn install一下,先放到本地仓库再说
                                mvn docker:build命令创建镜像,如果还要上传,加个-DpushImage参数
                                mvn clean package docker:build 只执行 build 操作
                                mvn clean package docker:build -DpushImage 执行 build 完成后 push 镜像
                                mvn clean package docker:build -DpushImageTag 执行 build 并 push 指定 tag 的镜像
                            -->
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
View Code

注意很坑的一个地方:

(2.1)pom.xml里一定不能配置跳过测试 否则target下不会生成surefire-reports目录
<skipTests>false</skipTests>
(2.2)Junit测试类中@Test注解必须是org.junit.Test

(3)编写pipeline

pipeline{
    agent any
    options {
        ansiColor('xterm')
    }
    stages{
        stage("单元测试"){
            steps {
                sh "mvn package -Dmaven.test.skip=false"
            }
        }
    }
    //将JUnit步骤放在post always中 当测试环境不通过时 依然可以收集到测试报告
    post{
        always{
            script{
                junit "**/target/surefire-reports/*.xml"
            }
        }
    }
}

查看测试报告:

参考:
Jenkins 集成单元测试报告
用插件MAVEN-SUREFIRE-REPORT-PLUGIN生成HTML格式测试报告

2、Jenkins集成JaCoCo检查代码覆盖率

(1)安装JaCoCo插件
(2)maven项目中引入jacoco插件

<plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.3</version>
                <configuration>
                    <skip>false</skip>
                    <destFile>target/coverage-reports/jacoco.exec</destFile>
                </configuration>
                <executions>
                    <execution>
                        <id>prepare-agent</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>report</id>
                        <phase>prepare-package</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/coverage-reports/jacoco.exec</dataFile>
                            <!--输出报告路径-->
                            <outputDirectory>target/coverage-reports</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

完整pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>com.macro.mall</groupId>
    <artifactId>mall-tiny</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>mall-tiny</name>
    <description>mall-tiny project</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <skipTests>false</skipTests>
        <!--该方法有漏洞,容易被黑客远程放入挖矿机镜像,开启需做好防范,
        年少无知的我,开放2375,没做ip限制,被拉去挖矿了。。。
        推荐使用CA加密端口-->
        <docker.host>http://192.168.118.106:2375</docker.host>
        <docker.maven.plugin.version>1.2.2</docker.maven.plugin.version>
        <druid.version>1.1.10</druid.version>
        <hutool.version>4.5.7</hutool.version>
        <swagger2.version>2.9.2</swagger2.version>
        <swagger-models.version>1.6.0</swagger-models.version>
        <swagger-annotations.version>1.6.0</swagger-annotations.version>
        <mysql-connector.version>8.0.16</mysql-connector.version>
        <jjwt.version>0.9.0</jjwt.version>
        <mybatis-plus.version>3.3.2</mybatis-plus.version>
        <velocity.version>2.2</velocity.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <dependencies>
        <!--SpringBoot通用依赖模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--SpringBoot校验框架-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <!--集成druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <!--Mysql数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector.version}</version>
        </dependency>
        <!--Swagger-UI API文档生产工具-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger2.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger2.version}</version>
        </dependency>
        <!--解决Swagger 2.9.2版本NumberFormatException-->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>${swagger-models.version}</version>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>${swagger-annotations.version}</version>
        </dependency>
        <!--redis依赖配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--SpringSecurity依赖配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--Hutool Java工具包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
        <!--JWT(Json Web Token)登录支持-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jjwt.version}</version>
        </dependency>
        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--SpringBoot配置处理-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!--MyBatis Plus 依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!--MyBatis Plus 代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!--Velocity模板引擎-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>${velocity.version}</version>
        </dependency>
        <!-- jacoco jar包引用 -->
        <dependency>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.8.3</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <!--表示执行任何子目录下所有命名以Tests结尾的java类 -->
                    <includes>
                        <include>**/*Tests.java</include>
                    </includes>
                    <!--表示不执行任何子目录下所有命名以Test开头的java类 -->
                    <!--
                    <excludes>
                        <exclude>**/Test*.java</exclude>
                    </excludes>
                    -->
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.3</version>
                <configuration>
                    <skip>false</skip>
                    <destFile>target/coverage-reports/jacoco.exec</destFile>
                </configuration>
                <executions>
                    <execution>
                        <id>prepare-agent</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>report</id>
                        <phase>prepare-package</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/coverage-reports/jacoco.exec</dataFile>
                            <!--输出报告路径-->
                            <outputDirectory>target/coverage-reports</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!-- docker-maven-plugin 插件就是为了帮助我们在Maven工程中,通过简单的配置,自动生成镜像并推送到仓库中。
                微服务部署有两种方法:
                (1)手动部署:首先基于源码打包生成jar包(或war包),写个Dockerfile文件,基于基础镜像搞个新镜像,将jar包(或war包)上传至虚拟机并拷贝至JDK容器。(太麻烦了)
                (2)通过Maven插件自动部署。 这也是企业实际开发中经常使用的方法。
                参考:
                https://blog.csdn.net/weixin_44424668/article/details/104062822
                https://www.cnblogs.com/jpfss/p/10945324.html
             -->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>${docker.maven.plugin.version}</version>
                <executions>
                    <execution>
                        <id>build-image</id>
                        <phase>package</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!--修改imageName节点的内容,改为私有仓库地址和端口,再加上镜像id和TAG,我们要直接传到私服-->
                    <!--配置最后生成的镜像名,docker images里的,我们这边取项目名:版本-->
                    <imageName>192.168.82.46:18083/${project.artifactId}:${project.version}</imageName>
                    <dockerHost>${docker.host}</dockerHost>
                    <!--基础镜像,相当于Dockerfile里的from-->
                    <baseImage>ascdc/jdk8</baseImage>
                    <!--入口点,project.build.finalName就是project标签下的build标签下的filename标签内容,testDocker-->
                    <!--相当于启动容器后,会自动执行java-jar/testDocker.jar-->
                    <entryPoint>["java", "-jar","/${project.build.finalName}.jar"]</entryPoint>
                    <!--是否推送到docker私有仓库  设置为true 每次构建都要推送镜像-->
                    <!--<pushImage>true</pushImage>-->
                    <!-- 要在$MAVEN_HOME/settings.xml中配置对应的serverId才行 否咋报错Fail : no basic auth credentials -->
                    <serverId>cicd-docker-release</serverId>
                    <registryUrl>192.168.82.46:18083</registryUrl>
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <!--
                                把哪个文件上传到docker,相当于Dockerfile里的add testDocker.jar
                                mvn clean,mvn install一下,先放到本地仓库再说
                                mvn docker:build命令创建镜像,如果还要上传,加个-DpushImage参数
                                mvn clean package docker:build 只执行 build 操作
                                mvn clean package docker:build -DpushImage 执行 build 完成后 push 镜像
                                mvn clean package docker:build -DpushImageTag 执行 build 并 push 指定 tag 的镜像
                            -->
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
View Code

(3)编写pipeline

 

pipeline{
    agent any
    options {
        ansiColor('xterm')
    }
    stages{
        stage("单元测试"){
            steps {
                sh "mvn clean install -Dmaven.test.skip=false"
                jacoco(
                        //代码覆盖率统计执行文件路径 Ant风格路径
                        execPattern: 'target/coverage-reports/jacoco.exec',
                        //class文件位置 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',
                        maximumComplexityCoverage: '70',
                        //方法覆盖率
                        minimumMethodCoverage: '30',
                        maximumMethodCoverage: '70',
                        //类覆盖率
                        minimumClassCoverage: '30',
                        maximumClassCoverage: '70',
                        //分支覆盖率
                        minimumBranchCoverage: '30',
                        maximumBranchCoverage: '70',
                        //如果为true 则只有所有维度的覆盖率变化量的绝对值小于小于相应的变化量阈值时构建结果才成功
                        buildOverBuild: true,
                        //各个维度覆盖率的变化量阈值
                        deltaInstructionCoverage: '80',
                        deltaLineCoverage: '80',
                        deltaComplexityCoverage: '80',
                        deltaMethodCoverage: '80',
                        deltaClassCoverage: '80',
                        deltaBranchCoverage: '80'
                )
            }
        }
    }
    //将JUnit步骤放在post always中 当测试环境不通过时 依然可以收集到测试报告
    post{
        always{
            script{
                junit "**/target/surefire-reports/*.xml"
            }
        }
    }

}

(4)构建并查看jaCoCo报告

构建失败 也会展示这个报告:

 

参考:
Jacoco+Jenkins持续集成配置覆盖率

二、性能测试
Taurus是一个开源的自动化框架,用于运行各种开源负载测试工具和功能测试工具,比如JMeter。Taurus关键特性:可以使用yaml或json来描述性能测试,符合test as code的理念;会根据选择的性能测试类型自动下载相应工具。在Jenkins使用Taurus需要安装插件Performance.

示例:使用Jenkins运行JMeter进行性能测试

(1)Jenkins安装Performance插件,就可以使用bzt指令

(2)Jenkins搭建Python环境

基于Taurus的性能测试是基于Python的,使用之前请务必确认系统中已经安装了Python环境。

(2.1)安装python、pip(Python包管理工具)、virtualenv(Python中虚拟环境管理工具)
Performance检测没有Taurus会在Jenkins工程下面创建一个虚拟环境taurus-venv,然后里面自动安装pip install bzt。

[root@node106 mall-tiny_master]# pwd
/data/.jenkins/workspace/mall-tiny_master
[root@node106 mall-tiny_master]# ll
total 72
-rw-r----- 1 root root   277 Sep 21 10:08 blaze_exist_jmeter_config.yml
drwxr-x--- 2 root root   276 Sep 21 10:08 images
-rw-r----- 1 root root   454 Sep 21 10:08 Jenkinsfile
-rw-r----- 1 root root 11349 Sep 21 10:08 LICENSE
-rw-r----- 1 root root 10070 Sep 21 10:08 mvnw
-rw-r----- 1 root root  6608 Sep 21 10:08 mvnw.cmd
-rw-r----- 1 root root 12640 Sep 21 10:08 pom.xml
-rw-r----- 1 root root 12453 Sep 21 10:08 README.md
drwxr-x--- 2 root root    27 Sep 21 10:08 sql
drwxr-x--- 4 root root    30 Sep 21 10:08 src
drwxr-x--- 4 root root    64 Sep 21 10:08 taurus-venv

参考:CentOS7下搭建Python3开发环境并创建虚拟环境

问题1:pip升级问题
You are using pip version 18.1, however version 20.2.3 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
解决方案:
python -m pip install --upgrade pip -i https://pypi.douban.com/simple

问题2:找不到安装包问题
ERROR: Could not find a version that satisfies the requirement virtualenvwrapper (from versions: none)
ERROR: No matching distribution found for virtualenvwrapper

换一下镜像源:/root/.pip/pip.conf

[global]
index-url = https://pypi.douban.com/simple

常见pip下载镜像还有:

阿里:https://mirrors.aliyun.com/pypi/simple
中国科学技术大学:https://pypi.mirrors.ustc.edu.cn/simple

(2.2)jenkins 安装Pyenv Pipeline插件,可以使用withPythonEnv(){}

(3)当前项目里新增Taurus配置文件
blaze_exist_jmeter_config.yml用于描述如何进行性能测试

execution:
  - scenario: simple
scenarios:
  simple:
      # SimpleTestPlan.jmx为测试计划 由jmeter工具生成
      script: SimpleTestPlan.jmx
modules:
  jmeter:
    # 注意:下载文件必须使用.zip后缀
    download-link: http://mirrors.tuna.tsinghua.edu.cn/apache/jmeter/binaries/apache-jmeter-{version}.zip
    version: 5.2.1

使用Jmeter生成jmx文件,参考:JMeter教程 

(4)当前项目新增Jenkinsfile编写pipeline

pipeline{
    agent any
    options {
        ansiColor('xterm')
    }
    stages{
        stage("单元测试"){
            steps {
                sh "mvn clean install -Dmaven.test.skip=false"
            }
        }
        stage("性能测试"){
            steps {
                // 可以指定bzt 而不是使用虚拟环境自己安装的bzt
                //sh "/usr/local/python3/bin/bzt 'blaze_exist_jmeter_config.yml'"
                bzt params: 'blaze_exist_jmeter_config.yml'
            }
        }
    }
    //将JUnit步骤放在post always中 当测试环境不通过时 依然可以收集到测试报告
    post{
        always{
            script{
                junit "target/surefire-reports/*.xml"
                allure([
                        includeProperties: false,
                        jdk: '',
                        properties: [],
                        reportBuildPolicy: 'ALWAYS',
                        results: [[path: 'target/allure-results']]
                ])
            }
        }
    }

}

参考:利用Jenkins pipeline配置测试工具

记录个问题:在虚拟环境目录taurus-venv下没有安装成功bzt,一直报错找不到bzt,怀疑是主机上python安装的有问题,手工创建虚拟环境,并在虚拟环境下安装bzt是没问题的

基于这个问题,只好将安装了一个全局bzt,并设置了环境变量/etc/profile中,主要需要重启Jenkins:

(5)界面展示性能测试报告

三、输出测试报告
Allure是一个非常美观的测试报告生成器。
(1)安装allure2-2.13.5.zip

(2)Jenkins配置allure安装地址

(3)项目pom.xml中引入allure

<?xml version="1.0" encoding="UTF-8"?>
<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>com.macro.mall</groupId>
    <artifactId>mall-tiny</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>mall-tiny</name>
    <description>mall-tiny project</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <skipTests>false</skipTests>
        <!--该方法有漏洞,容易被黑客远程放入挖矿机镜像,开启需做好防范,
        年少无知的我,开放2375,没做ip限制,被拉去挖矿了。。。
        推荐使用CA加密端口-->
        <docker.host>http://192.168.118.106:2375</docker.host>
        <docker.maven.plugin.version>1.2.2</docker.maven.plugin.version>
        <druid.version>1.1.10</druid.version>
        <hutool.version>4.5.7</hutool.version>
        <swagger2.version>2.9.2</swagger2.version>
        <swagger-models.version>1.6.0</swagger-models.version>
        <swagger-annotations.version>1.6.0</swagger-annotations.version>
        <mysql-connector.version>8.0.16</mysql-connector.version>
        <jjwt.version>0.9.0</jjwt.version>
        <mybatis-plus.version>3.3.2</mybatis-plus.version>
        <velocity.version>2.2</velocity.version>
        <aspectj.version>1.8.10</aspectj.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <dependencies>
        <!--SpringBoot通用依赖模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--SpringBoot校验框架-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <!--集成druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <!--Mysql数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector.version}</version>
        </dependency>
        <!--Swagger-UI API文档生产工具-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger2.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger2.version}</version>
        </dependency>
        <!--解决Swagger 2.9.2版本NumberFormatException-->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>${swagger-models.version}</version>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>${swagger-annotations.version}</version>
        </dependency>
        <!--redis依赖配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--SpringSecurity依赖配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--Hutool Java工具包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
        <!--JWT(Json Web Token)登录支持-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jjwt.version}</version>
        </dependency>
        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--SpringBoot配置处理-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!--MyBatis Plus 依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!--MyBatis Plus 代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!--Velocity模板引擎-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>${velocity.version}</version>
        </dependency>
        <!-- jacoco jar包引用 -->
        <dependency>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.8.3</version>
        </dependency>
        <!-- allure包引用 -->
        <dependency>
            <groupId>io.qameta.allure</groupId>
            <artifactId>allure-junit4</artifactId>
            <version>2.12.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.20</version>
                <configuration>
                    <!--表示执行任何子目录下所有命名以Test结尾的java类 -->
                    <includes>
                        <include>**/*Tests.java</include>
                    </includes>
                    <!--表示不执行任何子目录下所有命名以Test开头的java类 -->
                    <excludes>
                        <exclude>**/Test*.java</exclude>
                    </excludes>
                    <testFailureIgnore>false</testFailureIgnore>
                    <argLine>
                        -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                    </argLine>
                    <properties>
                        <property>
                            <name>listener</name>
                            <value>io.qameta.allure.junit4.AllureJunit4</value>
                        </property>
                    </properties>
                    <systemProperties> 
                        <property>
                            <name>allure.results.directory</name>
                            <value>${project.build.directory}/allure-results</value>
                        </property>
                        <property>
                            <name>allure.link.issue.pattern</name>
                            <value>https://example.org.issue/{}</value>
                        </property>
                    </systemProperties>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjweaver</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>io.qameta.allure</groupId>
                <artifactId>allure-maven</artifactId>
                <version>2.8</version>
            </plugin>
            <!-- docker-maven-plugin 插件就是为了帮助我们在Maven工程中,通过简单的配置,自动生成镜像并推送到仓库中。
                微服务部署有两种方法:
                (1)手动部署:首先基于源码打包生成jar包(或war包),写个Dockerfile文件,基于基础镜像搞个新镜像,将jar包(或war包)上传至虚拟机并拷贝至JDK容器。(太麻烦了)
                (2)通过Maven插件自动部署。 这也是企业实际开发中经常使用的方法。
                参考:
                https://blog.csdn.net/weixin_44424668/article/details/104062822
                https://www.cnblogs.com/jpfss/p/10945324.html
             -->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>${docker.maven.plugin.version}</version>
                <executions>
                    <execution>
                        <id>build-image</id>
                        <phase>package</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!--修改imageName节点的内容,改为私有仓库地址和端口,再加上镜像id和TAG,我们要直接传到私服-->
                    <!--配置最后生成的镜像名,docker images里的,我们这边取项目名:版本-->
                    <imageName>192.168.82.46:18083/${project.artifactId}:${project.version}</imageName>
                    <dockerHost>${docker.host}</dockerHost>
                    <!--基础镜像,相当于Dockerfile里的from-->
                    <baseImage>ascdc/jdk8</baseImage>
                    <!--入口点,project.build.finalName就是project标签下的build标签下的filename标签内容,testDocker-->
                    <!--相当于启动容器后,会自动执行java-jar/testDocker.jar-->
                    <entryPoint>["java", "-jar","/${project.build.finalName}.jar"]</entryPoint>
                    <!--是否推送到docker私有仓库  设置为true 每次构建都要推送镜像-->
                    <!--<pushImage>true</pushImage>-->
                    <!-- 要在$MAVEN_HOME/settings.xml中配置对应的serverId才行 否咋报错Fail : no basic auth credentials -->
                    <serverId>cicd-docker-release</serverId>
                    <registryUrl>192.168.82.46:18083</registryUrl>
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <!--
                                把哪个文件上传到docker,相当于Dockerfile里的add testDocker.jar
                                mvn clean,mvn install一下,先放到本地仓库再说
                                mvn docker:build命令创建镜像,如果还要上传,加个-DpushImage参数
                                mvn clean package docker:build 只执行 build 操作
                                mvn clean package docker:build -DpushImage 执行 build 完成后 push 镜像
                                mvn clean package docker:build -DpushImageTag 执行 build 并 push 指定 tag 的镜像
                            -->
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

 

(4)编写pipeline

pipeline{
    agent any
    options {
        ansiColor('xterm')
    }
    stages{
        stage("单元测试"){
            steps {
                sh "mvn clean install -Dmaven.test.skip=false"
            }
        }
    }
    //将JUnit步骤放在post always中 当测试环境不通过时 依然可以收集到测试报告
    post{
        always{
            script{
                junit "target/surefire-reports/*.xml"
                allure([
                        includeProperties: false,
                        jdk: '',
                        properties: [],
                        reportBuildPolicy: 'ALWAYS',
                        results: [[path: 'target/allure-results']]
                ])
            }
        }
    }

}

(5)构建并查看测试allure报告

问题1:allure要展示测试报告,首先要有测试用例,使用Junit注解或者allure注解;然后就是要有前面的单元测试(Junit或者JaCoCo)、性能测试;最后将结果展示出来;
关于如何使用allure注解,可以参考:jenkins + Allure配置  

问题2:关于because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified报错的解决方案
解决方案:代理模式的aspectj 

 

posted @ 2020-09-18 15:28  cac2020  阅读(3440)  评论(0编辑  收藏  举报