一文帶你揭秘如何采取非instrumentation啟動app,打造實時統計覆蓋率,一鍵觸發覆蓋率測試報告。
在上篇文章,一文帶你解決Android app手工測試或者自動化測試覆蓋率統計(擼代碼版),我們采用了instrumentation的方式去啟動app,很多人會問,如果我們不用instrumentation啟動app的方式,正常啟動app進行測試,然后收集覆蓋率可以嗎,答案,是可以的,如何做呢,下面帶你去揭曉其中的奧秘。
首先呢,我們還是基於我們的工作,去申請我們的讀寫的權限。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
申請后,我們在安裝app的時候一定要給予這兩個權限,接下來呢,我們去配置jacoco相關的。
在項目的build.gradle配置相關的,如下配置
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.8.4"
description("$buildDir/coverage.exec")
reportsDir = file("$buildDir/reports/jacoco")
}
首先我們去添加jacoco的插件,接着呢,我們去規定版本,然后去規定我們的覆蓋文件的位置,接下來我們去告知下測試報告的位置。這樣我們配置好了依賴,我們需要在debug打開覆蓋率。還是同一個的build.gradle配置
debug {
/**打開覆蓋率統計開關*/
testCoverageEnabled = true
minifyEnabled false //獲取代碼覆蓋率需要設為false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' }
配置完畢后呢,我們去編寫一個jacoco的工具類,用來處理覆蓋率文件的寫入。具體代碼如下
package com.example.studayapp.test;
import android.content.Context;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class JacocoUtils {
static String TAG = "JacocoUtils";
private static String DEFAULT_COVERAGE_FILE_PATH = "coverage.exec";
/**
* 生成ec文件
*
* @param isnew 是否重新創建exec文件
*/
public static void generateEcFile(boolean isnew) {
Log.d(TAG, "生成覆蓋率文件: " + DEFAULT_COVERAGE_FILE_PATH);
OutputStream out = null;
File mCoverageFilePath = new File(Environment.getExternalStorageDirectory(),DEFAULT_COVERAGE_FILE_PATH);
try {
if (isnew && mCoverageFilePath.exists()) {
Log.d(TAG, "清除舊的exec文件");
mCoverageFilePath.delete();
}
if (!mCoverageFilePath.exists()) {
mCoverageFilePath.createNewFile();
}
out = new FileOutputStream(mCoverageFilePath.getPath(), true);
//反射:獲取org.jacoco.agent.rt.IAgent
Object agent = Class.forName("org.jacoco.agent.rt.RT")
.getMethod("getAgent")
.invoke(null);
//反射:getExecutionData(boolean reset),獲取當前執行數據,
// 以jacoco二進制格式轉儲當前執行數據
// getExecutionData(boolean reset),reset如果為true,則之后清除當前執行數據
out.write((byte[]) agent.getClass().getMethod("getExecutionData", boolean.class)
.invoke(agent, false));
} catch (Exception e) {
Log.e(TAG, "generateEcFile: " + e.getMessage());
} finally {
if (out == null)
return;
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
我們通過反射:獲取org.jacoco.agent.rt.IAgent,然后,反射:getExecutionData(boolean reset),獲取當前執行數據,最后寫入執行的數據。
這樣我們收集的數據的腳本下好呢,那么我們應該怎么去收集我們的數據呢,之前的文章是通過系統的返回鍵后去生成的,這樣呢,其實在我們實際的工作中呢,是不常見呢,很多的時候呢,我們需要在特定的時候才去觸發呢,這里呢,我的做法呢,是在設置中,增加一個按鈕,生成測試覆蓋率的 按鈕來統一處理。
<Button
android:id="@+id/statistics"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="統計覆蓋率"></Button>
在布局文件呢,我們去創建一個按鈕,然后呢,我們去在這個按鈕去監聽點擊事件。
statistics=(Button) findViewById(R.id.statistics);
statistics.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
JacocoUtils.generateEcFile(true);
}
});
這樣呢,我們去安裝我們的app的debug版本

然后呢,我們去正常測試,最后呢,我們去點擊我們的按鈕。生成完畢后,如下。

我們去在項目的目錄下,我們去pull下來即可。
adb pull /sdcard/coverage.exec .
然后,我們在build.gradle創建一個任務。
def coverageSourceDirs = [
'../app/src/main/java'
]
task jacocoTestReport(type: JacocoReport) {
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
reports {
xml.enabled = true
html.enabled = true
}
classDirectories = fileTree(
dir: './build/intermediates/javac/debug/classes',
excludes: ['**/R*.class',
'**/*$InjectAdapter.class',
'**/*$ModuleAdapter.class',
'**/*$ViewInjector*.class'
])
sourceDirectories = files(coverageSourceDirs)
executionData = files("$buildDir/coverage.exec")
doFirst {
new File("$buildDir/intermediates/javac/debug/classes/").eachFileRecurse { file ->
if (file.name.contains('$$')) {
file.renameTo(file.path.replace('$$', '$'))
}
}
}
}
然后點擊

執行完畢后。

我們看下實際的效果

可以看到有測試報告,我們打開看下。

這是最后的覆蓋率測試的統計數據。
這里的數據呢,只是統計到了全量代碼的,還有增量代碼覆蓋率統計,多個覆蓋率文件的不同的如何進行組合。后續的文章會持續分享。

