-
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:
我將從以下幾點進行闡述:
- docker 底層文件開放變量
- 自助式jenkins JOB創建
- 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 <必填>項目倉庫地址,必須以git@開頭的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('git@gitlab.***.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="git@gitlab.*********************.git" def app_repo="git@gitlab.*********************.git" } options { //默認是啟用並發構建,disableConcurrentBuilds如果開啟則為禁用並發構建 // disableConcurrentBuilds() //保持構建的最大個數 buildDiscarder(logRotator(numToKeepStr: '20')) 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分支。后面可以選擇對應的分支了
好了 到此結束