本文的書寫已經(jīng)是很早之前的事情了腊嗡,只是把從新排版發(fā)了出來微猖,有部分內(nèi)網(wǎng)資源以及找不到了戒职,有興趣的同學可以根據(jù)原理補充上部分缺失的腳本秒紧。
覆蓋率統(tǒng)計說明
由于這段很重要绢陌,特此在最前面的章節(jié)說明。
在多項目工程中(dolphin和topnews都是多項目工程)熔恢,按照本文的方法僅能統(tǒng)計主工程的代碼覆蓋率脐湾,無法統(tǒng)計庫工程的代碼覆蓋率,如需統(tǒng)計庫工程的覆蓋率請參考在多項目工程中統(tǒng)計子工程的覆蓋率
Gradle
Gradle簡介
Gradle是一個開源的叙淌,以 Groovy 語言為基礎秤掌,面向Java應用為主≡淦校基于DSL(領域特定語言)語法的自動化構(gòu)建工具机杜,提供了強大的,可傳遞的依賴管理系統(tǒng)衅谷,目前的Dolphin和其他衍生產(chǎn)品都是使用Gradle進行編譯的椒拗,他的官網(wǎng)地址為:http://www.gradle.org
Gradle環(huán)境搭建
Gradle的版本在一直不停的更新中,截至發(fā)稿日期止获黔,最新的版本是2.3蚀苛。不同的Gralde版本在個別語法定義上會稍有不同(如代碼混淆2.1之前的版本為runProguard true,在之后的版本為minifyEnabled true)玷氏,在搭建編譯環(huán)境中最好和項目推薦的版本保持一致堵未。
Gradle環(huán)境的搭建很簡單,在官網(wǎng)下載對應版本的ZIP文件后解壓縮盏触,然后將bin目錄加到環(huán)境變量中或者將gradle軟鏈接到~/bin下即可
- 將bin目錄加到環(huán)境變量:用文本編輯器或者VIM等工具打開~/.bashrc文件渗蟹,在最后加上如下的內(nèi)容即可
export PATH=<bin目錄的絕對路徑>:$PATH
- 將gradle軟鏈到~/bin目錄下:
ln -s <bin目錄下gradle文件的絕對路徑> ~/bin/gradle
Gradle腳本簡介
以下是一個簡單的Gradle腳本:
apply plugin: 'android'
apply plugin: "jacoco"
apply from: "$project.rootDir/DolphinBuild/common.gradle"
gradle.beforeProject { project ->
if (project.file('build.gradle').exists()) {
project.buildscript {
repositories {
maven {
name = "Baina Maven Proxy"
url = "http://mirrors.baina.com:8080/archiva/repository/internal"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:1.0.+'
}
}
}
}
android {
// update sdk version to 21, because some variables of LOLLIPOP are used
compileSdkVersion 21
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
instrumentTest.setRoot('$project.rootDir/DolphinRecordTest')
androidTest {
manifest.srcFile "$project.rootDir/DolphinRecordTest/AndroidManifest.xml"
java.srcDir "$project.rootDir/DolphinRecordTest/src"
res.srcDirs "$project.rootDir/DolphinRecordTest/res"
assets.srcDirs "$project.rootDir/DolphinRecordTest/assets"
}
}
defaultConfig {
testApplicationId "mobi.mgeek.TunnyBrowser.test"
testInstrumentationRunner "android.test.InstrumentationTestRunner"
}
buildTypes {
debug {
minifyEnabled true
proguardFile 'proguard-debug.cfg'
testCoverageEnabled true
}
release {
minifyEnabled true
proguardFile 'proguard-release.cfg'
}
}
jacoco {
version "0.7.1.201405082137"
}
}
jacoco {
toolVersion "0.7.1.201405082137"
}
dependencies {
androidTestCompile files("$project.rootDir/DolphinRecordTest/libs/robotium.jar")
}
task jacocoTestReport(type: JacocoReport, dependsOn: "connectedAndroidTest") {
def coverageSourceDirs = [
"src",
]
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
reports {
xml.enabled true
html.enabled true
}
classDirectories = fileTree(
dir: "./build/intermediates/classes/debug",
includes: ["com/dolphin/browser/popup/RatingPopup*",
"com/dolphin/browser/popup/PopupManager*",
"mobi/mgeek/TunnyBrowser/BrowserActivity*",
"mobi/mgeek/TunnyBrowser/DeferredTaskManager*"],
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/BuildConfig.*',
'**/Manifest*.*']
)
sourceDirectories = files(coverageSourceDirs)
additionalSourceDirs = files(coverageSourceDirs)
executionData = fileTree(
dir: "$buildDir/outputs/code-coverage/connected"
)
}
Gradle是基于Groovy的腳本,關于Groovy的具體內(nèi)容請參考:http://www.groovy-lang.org
接下來我們會對每個代碼塊做簡要的介紹
- apply plugin
應用Gradle插件赞辩,這些插件可能是Gradle內(nèi)置的雌芽,如jacoco,也可能是外部引入的辨嗽,如android - apply from
應用其他的gradle腳本世落,相當于import - android代碼塊
android gradle plugin定義了其DSL,具體內(nèi)容可以參考Google的官方文檔 - jacoco代碼塊
jacoco gradle plugin定義了其DSL糟需,具體內(nèi)容可以參考Gradle的官方文檔 - dependency代碼塊
定義了工程的外部依賴屉佳,androidTestCompile定義了androidTest工程的依賴谷朝,compile定義源碼工程的依賴。 - task代碼塊
定義了新的task武花,gradle的build是以task為單位的圆凰,用戶可以自定義task,也可以使用預置的task(如assemblDebug即是內(nèi)置的編譯debug版本的task)髓堪,運行
gradle assemblDebug
即可完成編譯debug版本的工作送朱,而我們自定義的這個jacocoTestReport task我們會在后面的Jacoco部分詳述
使用Gradle編譯測試APK
通常Android項目的測試工程就在項目目錄下的test目錄下,當然為了不干擾源代碼的結(jié)構(gòu)干旁,我們也可以將其放到和項目目錄并列的文件夾下驶沼,以Dolphin項目為例,我們將測試項目(AndroidRecordTest)放在和主工程DolphinBrowserEN平級的目錄下争群,在上面的介紹中我們知道gradle assemblDebug可以編譯APK的debug版本回怜,gradle assemblDebugTest便可以生成工程的測試工程,那么如何讓gradle腳本識別出我們的測試工程的源碼路徑呢换薄,在android代碼塊中有如下的描述:
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
instrumentTest.setRoot('$project.rootDir/DolphinRecordTest')
androidTest {
manifest.srcFile "$project.rootDir/DolphinRecordTest/AndroidManifest.xml"
java.srcDir "$project.rootDir/DolphinRecordTest/src"
res.srcDirs "$project.rootDir/DolphinRecordTest/res"
assets.srcDirs "$project.rootDir/DolphinRecordTest/assets"
}
}
defaultConfig {
testApplicationId "mobi.mgeek.TunnyBrowser.test"
testInstrumentationRunner "android.test.InstrumentationTestRunner"
}
}
- 在sourceSets塊中main塊中定義了源碼工程對應的各種目錄玉雾,androidTest塊則描述了測試工程對應的各種目錄(如果測試工程目錄為源碼工程下的test子目錄,可以將$project.rootDir/DolphinRecordTest部分替換成test即可)
- instrumentTest.setRoot定義了測試工程的根目錄
- defaultConfig塊中定義了測試APK的包名和使用的InstrumentationRunner
我們可以將測試工程的build腳本合入到主工程中轻要,也可以獨立的創(chuàng)建測試工程的build.gradle文件將其作為獨立的項目單獨編譯
Jacoco
Jacoco簡介
Jacoco是一個開源的測試代碼覆蓋率的框架复旬,所謂代碼覆蓋率及在執(zhí)行手動或自動化用例時同時記錄源代碼中每一行/沒一個分支是否都被執(zhí)行到了,以此來從一個方面反映測試用例是否覆蓋了足夠多的邏輯分支冲泥,我們引入Jacoco到我們的Dolphin工程中以期幫助手工用例差漏補缺已經(jīng)檢查白盒用例的完備性
Jacoco集成
首先需要集成jacoco plugin:
apply plugin: "jacoco"
之后在android塊中聲明使用的Jacoco的版本即可驹碍,同時在對應的BuildType中開啟覆蓋率統(tǒng)計
android {
jacoco {
version "0.7.1.201405082137"
}
buildTypes {
debug {
testCoverageEnabled true
}
}
}
這樣編譯出的Debug版本便被成功插樁可以用于覆蓋率的統(tǒng)計了
覆蓋率測試的執(zhí)行
覆蓋率測試工具的設計本身是為了檢驗自動化腳本的覆蓋率的,做自動化執(zhí)行時可以通過選項設置將經(jīng)過Jacoco插樁的Build包的執(zhí)行記錄記錄下來凡恍,生成文件志秃,和源碼對比后生成覆蓋率報告,因此我們先來簡單講講如何驗證自動化腳本的代碼覆蓋率
- 確保被測源碼已經(jīng)集成了Jacoco并編譯其Debug版本
- 編譯測試用的自動化腳本(可以使用Eclipse編譯嚼酝,或者按照之前的介紹將其Gradle化后使用Gralde編譯)
- 將前面兩步生成的APK安裝到手機上
- 在手機上執(zhí)行如下的命令浮还,即可開始運行自動化腳本,并在生成覆蓋率文件/sdcard/coverage.ec:
adb shell am instrument -w -e coverage true -e coverageFile /sdcard/coverage.ec mobi.mgeek.TunnyBrowser.test/android.test.InstrumentationTestRunner
其中最后一段為測試目標package和使用的TestRunner闽巩,可以使用如下命令查詢當前已安裝的全部測試APK及其對應的TestRunner:
adb shell pm list instrumentation
當然你也可以指定執(zhí)行測試類或者TestSuite同樣也是-e參數(shù)钧舌,key為class,value為被測類或TestSuite涎跨,甚至可以僅僅測試某個類中的某個方法(如僅需測試特定類的全部方法延刘,不帶#及其后的方法名即可):
adb shell am instrument -w -e coverage true -e coverageFile /sdcard/coverage.ec -e class free.dante.coverage.TC#testDemo mobi.mgeek.TunnyBrowser.test/android.test.InstrumentationTestRunner
- 將生成的覆蓋率文件(在上面的例子中為/sdcard/coverage.ec)從手機中提取出來,準備后面的報告生成
adb pull /sdcard/coverage.rc
如果我們需要測試的是手工用例的代碼覆蓋率六敬,我們需要一個小小的HACK手段,由于無法直接實現(xiàn)手動執(zhí)行的覆蓋率文件的生成驾荣,我們需要一個自動化腳本的引導文件幫助我們啟動Dolphin和生成覆蓋率文件外构,自動化組已經(jīng)基本解決了問題.
下面講一講如何不錄制測試腳本普泡,直接手工執(zhí)行用例。
原理:
- jacoco的覆蓋率統(tǒng)計開關是在執(zhí)行測試用例的時候開啟的审编,我們?nèi)匀浑x不開測試工程撼班,但是我們完全可以用一個空白的自動化case來讓統(tǒng)計功能開啟,然后我們就可以再空白case的執(zhí)行期間手工操作垒酬;
- 在\share\membershare\hxiong\DolphinRecordTest\src\free\dante\coverage下有一個TC.java砰嘁,這就是我們的空白case,它會一直去查找sdcard/jacoco/文件夾里有沒有一些特定的文件勘究,找不到則一直sleep然后再找矮湘,找到了就會結(jié)束掉case;
- 所以我們就可以執(zhí)行該case口糕,通過adb push文件來主動的控制該case的空白時間直到我們需要結(jié)束海豚缅阳;
- 你在執(zhí)行全功能某一個模塊的用例時,需要N次退出海豚景描,那么你就需要N條這樣的空白case十办;
- 請將\share\membershare\hxiong\DolphinRecordTest\src\free\dante\coverage下的DolphinRecordTest工程(為你準備好了的測試工程)拖到你本地,請運行DolphinRecordTest/src/free/dante/coverage/下的initTestcases.py超棺,如果是window系統(tǒng)則直接雙擊,若是ubuntu則sudo python initTestcases.py向族,運行后會看到命令行里提示輸入一個數(shù),這個數(shù)就是你想要的空白case數(shù)棠绘,記住它也是你能退出海豚的次數(shù)限制件相;
- 運行完initTestcases.py后,你會發(fā)現(xiàn)在DolphinRecordTest/src/free/dante/coverage/下以TC開頭(寫死了)的java文件就是N個弄唧,然后請將測試工程打包成apk吧适肠;
- 當你adb shell am instrument xxxxxx運行測試工程后,請運行controll.py候引,它是一個偽控制流程腳本侯养,運行后會在命令行里看到當前是第幾個空白case及提示你輸入指令來結(jié)束掉當前的case進入下一條自動化case,或者結(jié)束掉所有的自動化case澄干。對了逛揩,這里需要注意,若是在ubuntu上運行該腳本麸俘,則不用加sudo辩稽,直接python controll.py
Jacoco報告的生成
在通過之前的步驟獲取到了覆蓋率文件后,我們可以通過一個gradle build來將這些覆蓋率文件生成最終的XML和HTML格式的覆蓋率報告从媚,如果有多個覆蓋率文件逞泄,生成報告時會自動Merge所有的執(zhí)行內(nèi)容,生成一份報告。
由于需要源碼做分析喷众,我們必須在源碼工程的gradle.build中添加生成Jacoco報告的task各谚,task內(nèi)容在上面已經(jīng)有了,我們再貼一遍并簡述一下各個參數(shù)及其用法
task jacocoTestReport(type: JacocoReport, dependsOn: "connectedAndroidTest") {
def coverageSourceDirs = [
"src",
]
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
reports {
xml.enabled true
html.enabled true
}
classDirectories = fileTree(
dir: "./build/intermediates/classes/debug",
includes: ["com/dolphin/browser/popup/RatingPopup*",
"com/dolphin/browser/popup/PopupManager*",
"mobi/mgeek/TunnyBrowser/BrowserActivity*",
"mobi/mgeek/TunnyBrowser/DeferredTaskManager*"],
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/BuildConfig.*',
'**/Manifest*.*']
)
sourceDirectories = files(coverageSourceDirs)
additionalSourceDirs = files(coverageSourceDirs)
executionData = fileTree(
dir: "$buildDir/outputs/code-coverage/connected"
)
}
- task的類型為JacocoReport到千,JacocoReport是Gradle內(nèi)置的一個類型昌渤,該類型的task用于生成Jacoco覆蓋率報告
- dependsOn: "connectedAndroidTest":這段是表明該task需要在connectedAndroidTest task完成之后進行,connectedAndroidTest是系統(tǒng)內(nèi)置的自動運行測試工程的task憔四,會默認在連接到電腦的設備上(有且僅有1臺膀息,否則會報錯)執(zhí)行全部的測試方法,如果dependsOn的task執(zhí)行失敗了則不會執(zhí)行我們定義的task了赵。由于我們使用的是adb命令運行測試腳本潜支,因此不要添加這部分,直接寫成task jacocoTestReport(type: JacocoReport){}即可
- reports代碼塊定義各種報告類型的開關斟览,我們在這里開啟了XML和HTML格式的報告輸出
- classDirectories代碼塊定義了生成報告使用的目標文件類毁腿,他的參數(shù)是一個FileCollection類型,我們可以使用FileTree來定義它苛茂,dir為目錄名已烤,includes后面為需要在報告中顯示的文件,excludes為不需要在報告中顯示的文件妓羊,如果不帶includes及其參數(shù)會使用dir下的全部文件胯究,否則需要按照其后的參數(shù)進行匹配僅使用符合匹配的文件,如果帶excludes參數(shù)則會從已被選中的文件中在排除掉匹配其后參數(shù)的文件躁绸。<color red>目標目錄需要使用編譯后的class文件裕循,即./build/intermediates/classes/debug下的文件,而不是JAVA源碼文件</color>
- executionData代碼塊定義了要被統(tǒng)計的覆蓋率文件的路徑净刮,該路徑下的全部文件都會被用于覆蓋率的計算
- 設置完成后運行該task即可生成Jacoco代碼覆蓋率報告剥哑,報告生成的路徑為:./build/reports/jacoco/<task名>,其下有XML和HTML兩份報告淹父,HTML的報告長這樣的: