<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # Android Coverage Guide ## 要求 * 硬件要求:Android 覆蓋率需要使用模擬器或者 root 過的機器。 * 你需要使用UiAutomator2構建你的應用。也就是說,你要在你的Android項目中實現Instrumentation的子類。Instrumentation將實現對代碼覆蓋情況的收集。 * 由于Instrumentation的數據存在于內存中,因此你還需要實現一個BroadCastReceiver,用于在Instrumentation結束時將Instrument結果輸出到手機存儲器的文件中。 ## 項目結構 你的項目需要看起來類似如下的結構 ``` src/main/java/com/example/pkg |____ MainActivity.java 你的主Activity |____ InstrumentActivityListener.java 自定義的用于實現覆蓋率導出到文件的接口 |____ InstrumentActivity.java 專門用于覆蓋率調試的Activity,你也可以直接用MainActivity。它將包含一個InstrumentActivityListener,并且在Activity結束時調用這個Listener以導出覆蓋率。你在調試時可以使用 |____ JacocoInstrumentation.java 你自己的Instrumentation文件,必須實現InstrumentActivityListener |____ EndEmmaBroadCast.java 用于接受結束信號廣播的接收器,它將調用InstrumentActivityListener并導出覆蓋率。 ``` 你在配置Caps時要做如下設置 * automationName : `uiautomator2` (無視大小寫) * androidCoverage : {package}/{instrumentation class}, 在我們的例子中是com.example.pkg/com.example.pkg.JacocoInstrumentation * appWaitActivity : 用作Insutrment的Activity的全名,在我們的例子中是com.example.pkg.InstrumentActivity * appWaitPackage : {package},在我們的例子中是com.example.pkg * androidCoverageEndIntent : 用作將當前coverage輸出至文件中的BroadCasterReceiver的Action名,在我們的例子中是 `com.example.pkg.END_EMMA` 工作原理 Appium 會用類似的命令啟動應用:`adb shell am instrument -e coverage true -w com.example.pkg/com.example.pkg.JacocoInstrumentation` 在測試完成后,會用`adb shell am broadcast -a com.example.pkg.END_EMMA` 使覆蓋率可以被收集(前提是你親自實現它) ## 例子:實現流程 ### [1] Appium測試項目 - 設置Caps 請參考 **『項目結構』 -> 『你在配置Caps時要做如下設置』** ### [2] Android項目 - AndroidManifest.xml 在 `AndroidManifest.xml` 里定義 instrumentation 和 broadcast 接收器。 ```xml <instrumentation android:name="com.example.pkg.instrumentation.JacocoInstrumentation" android:targetPackage="com.example.pkg" > </instrumentation> <!-- adb shell am broadcast -a com.example.pkg.END_EMMA --> <receiver android:name="com.example.pkg.EndEmmaBroadcast" > <intent-filter> <action android:name="com.example.pkg.END_EMMA" /> </intent-filter> </receiver> ``` 接下來,編寫 `EndEmmaBroadcast.java` : ```java package com.example.pkg; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Process; // adb shell am broadcast -a com.example.pkg.END_EMMA public class EndEmmaBroadcast extends BroadcastReceiver { InstrumentActivityListener activityListener; public void setInstrumentActivityListener(InstrumentActivityListener listener){ this.activityListener = listener; } @Override public void onReceive(Context context, Intent intent) { if(this.activityListener!=null){ activityListener.onActivityEnd(); } // once coverage is dumped, the processes is ended. Process.killProcess(Process.myPid()); } } ``` 之后,編寫 `JacocoInstrumentation.java`: ```java package com.example.pkg; import android.app.Activity; import android.app.Instrumentation; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Looper; import android.util.Log; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; public class JacocoInstrumentation extends Instrumentation implements InstrumentActivityListener { public static String TAG = "JacocoInstrumentation:"; private static String DEFAULT_COVERAGE_FILE_PATH = null; private final Bundle mResults = new Bundle(); private Intent mIntent; private static final boolean LOGD = true; private boolean mCoverage = true; private String mCoverageFilePath; public JacocoInstrumentation() { } @Override public void onCreate(Bundle arguments) { Log.d(TAG, "onCreate(" + arguments + ")"); super.onCreate(arguments); // bad notation, better use NAME+TimeSeed because you might generate more than 1 corage file DEFAULT_COVERAGE_FILE_PATH = getContext().getFilesDir().getPath().toString() + "/coverage.ec"; File file = new File(DEFAULT_COVERAGE_FILE_PATH); if(!file.exists()){ try{ file.createNewFile(); }catch (IOException e){ Log.d(TAG,"File Exception :"+e); e.printStackTrace();} } if(arguments != null) { mCoverageFilePath = arguments.getString("coverageFile"); } mIntent = new Intent(getTargetContext(), InstrumentActivity.class); mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); start(); } @Override public void onStart() { super.onStart(); Looper.prepare(); // Register broadcast receiver and start InstrumentActivity InstrumentActivity activity = (InstrumentActivity) startActivitySync(mIntent); EndEmmaBroadcast broadcast = new EndEmmaBroadcast(); activity.setInstrumentActivityListener(this); broadcast.setInstrumentActivityListener(this); activity.registerReceiver(broadcast, new IntentFilter("com.example.pkg.END_EMMA")); } private String getCoverageFilePath() { if (mCoverageFilePath == null) { return DEFAULT_COVERAGE_FILE_PATH; } else { return mCoverageFilePath; } } private void generateCoverageReport() { Log.d(TAG, "generateCoverageReport():" + getCoverageFilePath()); OutputStream out = null; try { out = new FileOutputStream(getCoverageFilePath(), false); Object agent = Class.forName("org.jacoco.agent.rt.RT") .getMethod("getAgent") .invoke(null); out.write((byte[]) agent.getClass().getMethod("getExecutionData", boolean.class) .invoke(agent, false)); } catch (Exception e) { Log.d(TAG, e.toString(), e); } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Override public void onActivityEnd() { if (LOGD) Log.d(TAG, "onActivityFinished()"); if (mCoverage) { generateCoverageReport(); } finish(Activity.RESULT_OK, mResults); } } ``` 之后是`InstrumentActivityListener.java` ```java package com.example.pkg; public interface InstrumentActivityListener { void onActivityEnd(); } ``` 可選的 `InstrumentActivity.java` ```java package com.example.pkg; import android.app.Instrumentation; import android.os.Bundle; import android.util.Log; public class InstrumentActivity extends MainActivity { public static String TAG = "IntrumentedActivity"; private InstrumentActivityListener listener; public void setInstrumentActivityListener(InstrumentActivityListener listener) { this.listener = listener; } // Generate output report when the activity is destroyed @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy()"); super.finish(); if (listener != null) { listener.onActivityEnd(); } } } ``` 最后,最重要的是`gradle`: ```groovy .... apply plugin: 'jacoco' // add plugin for jacoco ... android { ... defaultConfig { ... testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { ... } debug{ testCoverageEnabled = true } } } dependencies { ... //uiautomator androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.0' } ``` ### [3] 現在,你可以構建apk,并拿來跑你的自動測試吧!! 測試完成后,就會在/data/data/com.example.pkg/files中生成coverage.ec文件,將其pull出。 ### [4] 關于拉出HTML報告 當你跑完測試后,程序會在你手機的app里產生coverage.ec這樣的文件。 * [1] 首先,利用adb pull把coverage.ec拉出手機 * [2] 關于如何把ec轉化成HTML,你可以在gradle里加入下列task: ```groovy def coverageSourceDirs = [ './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/classes/debug', excludes: ['**/R*.class', '**/*$InjectAdapter.class', '**/*$ModuleAdapter.class', '**/*$ViewInjector*.class' ]) sourceDirectories = files(coverageSourceDirs) // NOTE: Put your ec file here executionData = files("SOME PATH/coverage.ec") doFirst { new File("$buildDir/intermediates/classes/").eachFileRecurse { file -> if (file.name.contains('$$')) { file.renameTo(file.path.replace('$$', '$')) } } } } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看