Android手工測試獲取代碼覆蓋率解決方案

最近需要做一個功能,就是Android手工測試之後能夠直接獲取覆蓋率。當我們都用同一個版本的時候我們可以merge對應的覆蓋率變成了一個整體的代碼覆蓋率,jacoco就可以幫我們做到。具體怎麼說呢,我們來看下。

首先我們新建一個項目:

新增三個文件作為監聽:

FinishListener:

public interface FinishListener {n void onActivityFinished();n void dumpIntermediateCoverage(String filePath);n}nnnInstrumentedActivitynnpublic class InstrumentedActivity extends MainActivity {n public static String TAG = "IntrumentedActivity";n private com.xxx.xxx mListener;n public void setFinishListener(FinishListener listener) {n mListener = listener;n }n @Overriden public void onDestroy() {n Log.d(TAG + ".InstrumentedActivity", "onDestroy()");n super.finish();n if (mListener != null) {n mListener.onActivityFinished();n }n }n}n

JacocoInstrumentation:npublic class JacocoInstrumentation extends Instrumentation implementsn FinishListener {n public static String TAG = "JacocoInstrumentation:";n private static String DEFAULT_COVERAGE_FILE_PATH = "/mnt/sdcard/coverage.ec";nn private final Bundle mResults = new Bundle();nn private Intent mIntent;n private static final boolean LOGD = true;nn private boolean mCoverage = true;nn private String mCoverageFilePath;nnn /**n * Constructorn */n public JacocoInstrumentation() {nn }nn @Overriden public void onCreate(Bundle arguments) {n Log.d(TAG, "onCreate(" + arguments + ")");n super.onCreate(arguments);n DEFAULT_COVERAGE_FILE_PATH = getContext().getFilesDir().getPath().toString() + "/coverage.ec";nn File file = new File(DEFAULT_COVERAGE_FILE_PATH);n if (!file.exists()) {n try {n file.createNewFile();n } catch (IOException e) {n Log.d(TAG, "異常 : " + e);n e.printStackTrace();n }n }n if (arguments != null) {n //mCoverage = getBooleanArgument(arguments, "coverage");n mCoverageFilePath = arguments.getString("coverageFile");n }nn mIntent = new Intent(getTargetContext(), InstrumentedActivity.class);n mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);n start();n }nn @Overriden public void onStart() {n if (LOGD)n Log.d(TAG, "onStart()");n super.onStart();nn Looper.prepare();n InstrumentedActivity activity = (InstrumentedActivity) startActivitySync(mIntent);n activity.setFinishListener(this);n }nn private boolean getBooleanArgument(Bundle arguments, String tag) {n String tagString = arguments.getString(tag);n return tagString != null && Boolean.parseBoolean(tagString);n }nnn private void generateCoverageReport() {n Log.d(TAG, "generateCoverageReport():" + getCoverageFilePath());n OutputStream out = null;n try {n out = new FileOutputStream(getCoverageFilePath(), false);n Object agent = Class.forName("org.jacoco.agent.rt.RT")n .getMethod("getAgent")n .invoke(null);nn out.write((byte[]) agent.getClass().getMethod("getExecutionData", boolean.class)n .invoke(agent, false));n } catch (Exception e) {n Log.d(TAG, e.toString(), e);n } finally {n if (out != null) {n try {n out.close();n } catch (IOException e) {n e.printStackTrace();n }n }n }n }nn private String getCoverageFilePath() {n if (mCoverageFilePath == null) {n return DEFAULT_COVERAGE_FILE_PATH;n } else {n return mCoverageFilePath;n }n }nn private boolean setCoverageFilePath(String filePath){n if(filePath != null && filePath.length() > 0) {n mCoverageFilePath = filePath;n return true;n }n return false;n }nn private void reportEmmaError(Exception e) {n reportEmmaError("", e);n }nn private void reportEmmaError(String hint, Exception e) {n String msg = "Failed to generate emma coverage. " + hint;n Log.e(TAG, msg, e);n mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "nError: "n + msg);n }nn @Overriden public void onActivityFinished() {n if (LOGD)n Log.d(TAG, "onActivityFinished()");n if (mCoverage) {n generateCoverageReport();n }n finish(Activity.RESULT_OK, mResults);n }nn @Overriden public void dumpIntermediateCoverage(String filePath){n // TODO Auto-generated method stubn if(LOGD){n Log.d(TAG,"Intermidate Dump Called with file name :"+ filePath);n }n if(mCoverage){n if(!setCoverageFilePath(filePath)){n if(LOGD){n Log.d(TAG,"Unable to set the given file path:"+filePath+" as dump target.");n }n }n generateCoverageReport();n setCoverageFilePath(DEFAULT_COVERAGE_FILE_PATH);n }n }nn}n

然後manifest需要修改成:

<?xml version="1.0" encoding="utf-8"?>n<manifest xmlns:android="http://schemas.android.com/apk/res/android"n package="com.xxx.xxx">nn <applicationn android:allowBackup="true"n android:icon="@mipmap/ic_launcher"n android:label="@string/app_name"n android:supportsRtl="true"n android:theme="@style/AppTheme">n <activity android:name=".MainActivity">n <intent-filter>n <action android:name="android.intent.action.MAIN" />nn <category android:name="android.intent.category.LAUNCHER" />n </intent-filter>n </activity>nn <activity android:label="InstrumentationActivity"n android:name="com.xxx.xxx.test.InstrumentedActivity" />nn <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>nn </application>nn <instrumentationn android:handleProfiling="true"n android:label="CoverageInstrumentation"n android:name="com.xxx.xxx.test.JacocoInstrumentation"n android:targetPackage="com.xxx.xxx.codecoverage">n </instrumentation>n</manifest>n

在gradle的文件里需要修改:

buildTypes {n release {n minifyEnabled falsen proguardFiles getDefaultProguardFile(proguard-android.txt), proguard-rules.pron }n debug {n testCoverageEnabled = truen }nn }n}n

同時需要增加:

def coverageSourceDirs = [n../app/src/main/javan]nntask jacocoTestReport(type: JacocoReport) {n group = "Reporting"n description = "Generate Jacoco coverage reports after running tests."n reports {n xml.enabled = truen html.enabled = truen }n classDirectories = fileTree(ndir: ./build/intermediates/classes/debug,nexcludes: [**/R*.class,n**/*$InjectAdapter.class,n**/*$ModuleAdapter.class,n**/*$ViewInjector*.classn ])n sourceDirectories = files(coverageSourceDirs)n executionData = files("$buildDir/outputs/code-coverage/connected/coverage.ec")nn doFirst {nnew File("$buildDir/intermediates/classes/").eachFileRecurse { file ->nif (file.name.contains($$)) {n file.renameTo(file.path.replace($$, $))n }n }n }n

接著我們打包出來之後需要通過這個命令來啟動app:

adb shell am instrument com.xxx.xxx.codecoverage/com.xxx.xxx.test.JacocoInstrumentation

同時在gradle下面可以看到這個task:

我們運行task之後就可以看到代碼覆蓋率啦。

有啥問題隨時來找我哈

推薦閱讀:

GUI自動化測試進階:頁面對象模式
某測試模擬器性能優化-用vprof對Python程序性能調優
HiCircle的第一款產品TestBus beta版出來啦
使用 iMacros 來自動化日常的工作
手工測試如何轉向自動化測試

TAG:android自动化测试 | 软件测试 | 自动化测试 |