一文帶你揭祕如何採取非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('$$', '$')) } } } }
然後點選
執行完畢後。
我們看下實際的效果
可以看到有測試報告,我們開啟看下。
這是最後的覆蓋率測試的統計資料。
這裡的資料呢,只是統計到了全量程式碼的,還有增量程式碼覆蓋率統計,多個覆蓋率檔案的不同的如何進行組合。後續的文章會持續分享。