Android手工测试代码覆盖率增强版

发表于:2018-3-28 09:29

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:doctorq    来源:CSDN

  加入代码覆盖率的文件
  从参考文章中下载文件,解压缩后有4个文件:
  .
  ├── EmmaInstrumentation.java
  ├── FinishListener.java
  ├── InstrumentedActivity.java
  └── SMSInstrumentedReceiver.java
  提取前三个文件加入到我们的源码中,我们的包名为com.wuba.wuxian.android_0504,代码覆盖率的三个文件存放在com.wuba.wuxian.test 包中。
  然后对三个文件做相应的修改,修改后的文件内容如下:
  FinishListener(无修改):
  public interface FinishListener {
      void onActivityFinished();
      void dumpIntermediateCoverage(String filePath);
  }
  JacocoInstrumentation(原EmmaInstrumentation文件):
  public class JacocoInstrumentation extends Instrumentation implements
          FinishListener {
      public static String TAG = "JacocoInstrumentation:";
      private static String DEFAULT_COVERAGE_FILE_PATH = "/mnt/sdcard/coverage.ec";
      private final Bundle mResults = new Bundle();
      private Intent mIntent;
      private static final boolean LOGD = true;
      private boolean mCoverage = true;
      private String mCoverageFilePath;
      /**
       * Constructor
       */
      public JacocoInstrumentation() {
      }
      @Override
      public void onCreate(Bundle arguments) {
          Log.d(TAG, "onCreate(" + arguments + ")");
          super.onCreate(arguments);
          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, "异常 : " + e);
                  e.printStackTrace();
              }
          }
          if (arguments != null) {
              //mCoverage = getBooleanArgument(arguments, "coverage");
              mCoverageFilePath = arguments.getString("coverageFile");
          }
          mIntent = new Intent(getTargetContext(), InstrumentedActivity.class);
          mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        
          start();
      }
      @Override
      public void onStart() {
          if (LOGD)
              Log.d(TAG, "onStart()");
          super.onStart();
          Looper.prepare();
          InstrumentedActivity activity = (InstrumentedActivity) startActivitySync(mIntent);
          activity.setFinishListener(this);
      }
      private boolean getBooleanArgument(Bundle arguments, String tag) {
          String tagString = arguments.getString(tag);
          return tagString != null && Boolean.parseBoolean(tagString);
      }
      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();
                  }
              }
          }
      }
      private String getCoverageFilePath() {
          if (mCoverageFilePath == null) {
              return DEFAULT_COVERAGE_FILE_PATH;
          } else {
              return mCoverageFilePath;
          }
      }
      private boolean setCoverageFilePath(String filePath){
          if(filePath != null && filePath.length() > 0) {
              mCoverageFilePath = filePath;
              return true;
          }
          return false;
      }
      private void reportEmmaError(Exception e) {
          reportEmmaError("", e);
      }
      private void reportEmmaError(String hint, Exception e) {
          String msg = "Failed to generate emma coverage. " + hint;
          Log.e(TAG, msg, e);
          mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "\nError: "
                  + msg);
      }
      @Override
      public void onActivityFinished() {
          if (LOGD)
              Log.d(TAG, "onActivityFinished()");
          if (mCoverage) {
              generateCoverageReport();
          }
          finish(Activity.RESULT_OK, mResults);
      }
      @Override
      public void dumpIntermediateCoverage(String filePath){
          // TODO Auto-generated method stub
          if(LOGD){
              Log.d(TAG,"Intermidate Dump Called with file name :"+ filePath);
          }
          if(mCoverage){
              if(!setCoverageFilePath(filePath)){
                  if(LOGD){
                      Log.d(TAG,"Unable to set the given file path:"+filePath+" as dump target.");
                  }
              }
              generateCoverageReport();
              setCoverageFilePath(DEFAULT_COVERAGE_FILE_PATH);
          }
      }
  }
  InstrumentedActivity:
  public class InstrumentedActivity extends MainActivity {
      public static String TAG = "InstrumentedActivity";
      private FinishListener mListener;
      public void setFinishListener(FinishListener listener) {
          mListener = listener;
      }
      @Override
      public void onDestroy() {
          Log.d(TAG + ".InstrumentedActivity", "onDestroy()");
          super.finish();
          if (mListener != null) {
              mListener.onActivityFinished();
          }
      }
  }
  修改AndroidManifest.xml
  <?xml version="1.0" encoding="utf-8"?>
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.wuba.wuxian.android_0504" >
      <!-- To access Google+ APIs: -->
      <uses-permission android:name="android.permission.INTERNET" />
      <!--
   To retrieve OAuth 2.0 tokens or invalidate tokens to disconnect a user. This disconnect
       option is required to comply with the Google+ Sign-In developer policies
      -->
      <uses-permission android:name="android.permission.USE_CREDENTIALS" /> <!-- To retrieve the account name (email) as part of sign-in: -->
      <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <!-- To auto-complete the email text field in the login form with the user's emails -->
      <uses-permission android:name="android.permission.READ_PROFILE" />
      <uses-permission android:name="android.permission.READ_CONTACTS" />
      <application
          android:allowBackup="true"
          android:icon="@mipmap/ic_launcher"
          android:label="@string/app_name"
          android:theme="@style/AppTheme" >
          <activity
              android:name=".MainActivity"
              android:label="@string/app_name" >
              <intent-filter>
                  <action android:name="android.intent.action.MAIN" />
                  <category android:name="android.intent.category.LAUNCHER" />
              </intent-filter>
          </activity>
          <meta-data
              android:name="com.google.android.gms.version"
              android:value="@integer/google_play_services_version" />
          <activity
              android:name=".GoActivity"
              android:label="@string/title_activity_go" >
          </activity>
          <activity android:label="InstrumentationActivity"
              android:name="com.wuba.wuxian.test.InstrumentedActivity" />
      </application>
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
      <instrumentation
          android:handleProfiling="true"
          android:label="CoverageInstrumentation"
          android:name="com.wuba.wuxian.test.JacocoInstrumentation"
          android:targetPackage="com.wuba.wuxian.android_0504"/>
  </manifest>
  执行
  首先构建APK,安装到手机上,默认android会打开应用,我们需要关闭,再通过一下命令打开应用:
  adb shell am instrument com.wuba.wuxian.android_0504/com.wuba.wuxian.test.JacocoInstrumentation
  打开应用后,你可以进行测试
  测试完成后,获得代码覆盖率数据
  测试完成后,我们要收集代码覆盖率的数据了。其实就是要生成一个jacoco的文件,在本次实验中,我们定义的jacoco文件问coverage.ec文件,存放在应用数据目录下files文件夹下,而且生成数据的操作放在应用退出时。
 
  你需要将这个coverage.ec文件dump到本地,然后利用代码结构生成报告,原生gradle-android-plugin也是这样一个流程。所以怎么生成报告我就不在详细介绍了。
  总结
  上面的方式是在不修改源码环境下做的一个类似于后门的方式获得代码覆盖率数据的方式,但是如果你想更加方便的去做这个,比如在应用中长按生成数据,你可以修改自己的源码,在长按事件中调用JacocoInstrumentation中generateCoverageReport方法就可以了。



上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号