1. 程式人生 > 實用技巧 >功能+自動化測試程式碼掃描(demo)

功能+自動化測試程式碼掃描(demo)

  • Jacoco 是一個開源的覆蓋率工具。Jacoco 可以嵌入到 Ant 、Maven 中,並提供了 EclEmma Eclipse 外掛,也可以使用 Java Agent 技術監控 Java 程式。很多第三方的工具提供了對 Jacoco 的整合,如 sonar、Jenkins、IDEA。

  • Jacoco 包含了多種尺度的覆蓋率計數器,包含指令級(Instructions,C0 coverage),分支(Branches,C1 coverage)、圈複雜度(Cyclomatic Complexity)、行(Lines)、方法(Non-abstract Methods)、類(Classes)

這是對jacoco 的功能和使用的簡介,我就不需要過多的描述。我的這篇文章就是一個對docker中服務的一個功能測試+自動測試覆蓋率統計的demo:

我將從以下幾點進行闡述:

  1. docker 底層檔案開放變數
  2. 自助式jenkins JOB建立
  3. Pipeline 自助式覆蓋率統計

一 docker 底層檔案開放變數:

這裡開放變數的作用是決定 自助式jenkins JOB 是否執行jacoco 覆蓋率程式碼掃描。因為我們是通過jacocoAgent 這種方式來實現程式碼覆蓋率掃描的。並不是所有的服務都需要

進行程式碼覆蓋率掃描,所以我們做成了這種引數化,方便自助決定是否進行覆蓋率掃描。

你可能會有疑問為什麼不寫死呢?答案是:我們的測試環境中的docker底層檔案用的是同一套。

開放變數的引數是:-javaagent:/usr/local/jacoco-agent.jar=includes=*,output=tcpserver,append=true,address=0.0.0.0,port=*****

這裡的這個引數傳遞是在 第二個環節自助式jenkins JOB 建立 中設定的一個輸入標籤

二 自助式jenkins JOB建立:

前期可愛的運維同事,幫助我們建立了一個通用的自助構建服務的模板:

  1、jobName <必填>選項,命名格式:環境-服務名

  2、service_name<必填>服務名稱,請對應gitlab上專案名稱和生成的jar包名稱

  3、app_repo<必填>專案倉庫地址,必須以[email protected]開頭的ssh地址

  4、jvm_opts(開放變數)<可選填>自定義jvm引數,除預設配置jvm引數之外的自定義jvm引數,預設為空 這裡由於我們需要執行jacoco-agent,所以輸入:-javaagent:/usr/local/jacoco-      agent.jar=includes=*,output=tcpserver,append=false,address=0.0.0.0,port=****

點選build,會自動生成build後job的地址

點選連結,自跳轉到job

點選build with parameters

如上圖所示 點選build with parameters後 1、2、3會自動填上之前的引數,這裡我們只需要選著4、5 部署的分支和部署環境就完成了自助式jenkins JOB建立。下面你就可以進行功能測試和自動化測試了。

接下來的重點是如何拉取程式碼覆蓋率報告:

這裡需要注意的是,jacocoAgent 記錄了程式碼執行的軌跡,如果想通過**.exec 獲取html報告,我們需要有對應分支的.class 檔案才可能實現。這裡參照sonar掃描一樣的方式獲取

class檔案。我們通過PipeLine的方式通過編譯對應分支的程式碼獲取class檔案。

三 Pipeline 自助式覆蓋率統計

這裡的的工作主要是通過build 獲取class檔案,通過.exec檔案生成覆蓋率報告在jenkins上展示出來

那麼是如何是實現的呢?

1、也需要運維像第一部一樣建立一個公共job 作用僅僅是build操作,而不需要deploy

2、需要在git上建立一個PipeLine公共指令碼 PipleLine 開放幾個公共標籤

標籤內容是:

  • 服務名稱:service_name
  •   務器地址:address
  •   編譯後生成的classes檔案相對路徑:classPattern
  • 剔除無需統計具體的classes檔案:exclusionPattern
  • 原始碼路徑:sourcePattern
  •   倉庫地址:app_repo

指令碼如下:

pipelineJob("$jobName") {
parameters {
stringParam("service_name", "${service_name}",'服務名稱')
stringParam('address', "${address}", '伺服器地址')
stringParam("classPattern", "${classPattern}", '編譯後生成的classes檔案相對路徑')
stringParam('exclusionPattern', "${exclusionPattern}", '剔除無需統計具體的classes檔案,多個以英文逗號,隔開。')
stringParam('sourcePattern', "${sourcePattern}", '原始碼路徑。')
stringParam('app_repo', "${app_repo}", '倉庫地址') gitParameter {
name('branch_name')
branch('')
type('PT_BRANCH')
defaultValue('master')
description('')
branchFilter('origin/(.*)')
quickFilterEnabled(true)
tagFilter('*')
sortMode('ASCENDING_SMART')
selectedValue('TOP')
useRepository("$app_repo")
} } definition {
cpsScm {
scm {
git {
remote {
url('[email protected]***.cn:***/jenkinspipeline.git')
credentials('*********************')
}
branch('*/master')
}
}
scriptPath("Jenkinsfile-Jacoco")
}
}
}

jenkinspipeline (git檔案)

pipeline {
agent {node {label 'k8s-slave'}} environment {
def JAVA_HOME="/usr/local/jdk"
def M2_HOME="/usr/local/maven"
def MAVEN_OPTS="-Xmx1024m" def PATH="/opt/kube/bin:/bin:/sbin/:/usr/bin:/usr/sbin/:/usr/local/bin:$PATH" def dingding_url="https://oapi.dingtalk.com/robot/send?access_token=**************************" def harbor_server="***************" def harbor_auth_id="**********************" def git_auth_id="*********************" def ansible_repo="[email protected]*********************.git" def app_repo="[email protected]*********************.git" } options {
//預設是啟用併發構建,disableConcurrentBuilds如果開啟則為禁用併發構建
// disableConcurrentBuilds()
//保持構建的最大個數
buildDiscarder(logRotator(numToKeepStr: ''))
ansiColor('xterm')
timestamps()
} parameters {
choice(name: 'service_name', choices: '*********************')
gitParameter(name: 'branch_name', branchFilter: 'origin/(.*)', defaultValue: 'master', type: 'PT_BRANCH', quickFilterEnabled: 'true', description: '選擇需要構建的分支', sortMode: 'ASCENDING_SMART')
} post{
success{
script { dingTalk accessToken: "${env.dingding_url}", imageUrl: '*********************', jenkinsUrl: "${env.BUILD_URL}",message: "應用${service_name}構建成功!",notifyPeople: '*********************' wrap([$class: 'BuildUser']) {
mail to: "${BUILD_USER_EMAIL}",
from: "*********************",
subject: "'${JOB_NAME}' 第${BUILD_NUMBER}次,構建結果通知【成功】",
body: "本次構建由 ${BUILD_USER} 發起,構建【成功】,構建版本 ${params.service_name}:${params.branch_name} .\n具體構建細節,可以前往${env.BUILD_URL}進行檢視。"
} } cleanWs() }
failure{
script { dingTalk accessToken: "${env.dingding_url}", imageUrl: '*********************', jenkinsUrl: "${env.BUILD_URL}",message:"應用${service_name}構建失敗!",notifyPeople: '*********************' wrap([$class: 'BuildUser']) {
mail to: "${BUILD_USER_EMAIL}",
from: "*********************",
subject: "'${JOB_NAME}' 第${BUILD_NUMBER}次,構建結果通知【失敗】",
body: "本次構建由 ${BUILD_USER} 發起,構建【失敗】 ,構建版本 ${params.service_name}:${params.branch_name} .\n具體構建細節,可以前往${env.BUILD_URL}進行檢視。"
}
}
}
unstable{
script {
wrap([$class: 'BuildUser']) {
mail to: "${BUILD_USER_EMAIL}",
from: "*********************",
subject: "'${JOB_NAME}' 第${BUILD_NUMBER}次,構建結果通知【失敗】",
body: "本次構建由 ${BUILD_USER} 發起,構建【失敗】,構建版本 ${params.service_name}:${params.branch_name} .\n具體構建細節,可以前往${env.BUILD_URL}進行檢視。"
}
}
}
} stages {
stage("獲取程式碼") {
parallel{
stage('配置構建資訊') {
steps {
script {
wrap([$class: 'BuildUser']){
currentBuild.description = "本次構建由<strong><span style='color:#E53333;'> ${BUILD_USER} </span></strong>發起,構建版本 <strong><span style='color:#E53333;'>${params.service_name}:${params.branch_name}</span></strong>"
}
}
}
} stage("獲取應用程式碼") {
steps {
echo "branch_name: ${params.branch_name}"
sh 'git config --global http.sslVerify false' wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'xterm']) {
dir ( "${env.WORKSPACE}" ) {
git (
branch: "${params.branch_name}",
credentialsId: "${env.git_auth_id}",
url: "${app_repo}"
)
}
}
}
} }
} stage("jacoco覆蓋率統計") {
steps {
dir("${env.WORKSPACE}") {
sh "pwd"
sh "mvn clean install -Dmaven.test.skip=true org.jacoco:jacoco-maven-plugin:0.8.2:dump -Djacoco.address=\"${params.address}\" -Djacoco.port=********************* -Djacoco.destFile=jacoco_payment.exec -Djacoco.reset=false"
jacoco(execPattern:'jacoco_payment.exec',classPattern:"${params.classPattern}",sourcePattern:"${params.sourcePattern}",exclusionPattern:"${params.exclusionPattern}") }
}
}
}
}

上面的PipeLine 配置好了 下面 看看 頁面

注:第一次只能選擇master分支。後面可以選擇對應的分支了

好了 到此結束