一文帶你揭秘如何采取非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('$$', '$')) } } } }
然后點擊
執行完畢后。
我們看下實際的效果
可以看到有測試報告,我們打開看下。
這是最后的覆蓋率測試的統計數據。
這里的數據呢,只是統計到了全量代碼的,還有增量代碼覆蓋率統計,多個覆蓋率文件的不同的如何進行組合。后續的文章會持續分享。