安卓-Jacoco-精准测试应用

Jacoco-精准测试

开源地址:https://github.com/seasonxie/JacocoAndroid

不依赖源码执行
接口测试也能直观看出覆盖度
知道什么样的用例保证哪块的代码,更好的精准度测试

 

 1.实现jacoco Instrumentation操作(后面通过命令直接启动该instrument,最下面有),注意最后启动了 InstrumentedActivity

public class JacocoInstrumentation extends Instrumentation

 @Override
    public void onCreate(Bundle arguments) {
        super.onCreate(arguments);
        DEFAULT_COVERAGE_FILE_PATH = "/sdcard/cover.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();
    }

 

2.在InstrumentedActivity  onDestroy设置监听,当触发的时候触发生成jacoco覆盖率文件

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  权限
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();
        }
    }

}

3.触发监听的操作

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();
                }
            }
        }
    }

 

启动:

2.1注册instrumentation,通过instrumentation启动被测应用

adb shell am instrument com.jacs.zhaotang.jscscs/test.JacocoInstrumentation

    <instrumentation
        android:handleProfiling="true"
        android:label="CoverageInstrumentation"
        android:name="test.JacocoInstrumentation"
        android:targetPackage="com.jacs.zhaotang.jscscs"/>

 

3.把生成的jacoco覆盖率文件放在指定文件夹,用gradle生成htmlreport

 将该文件拖至入app根目录/build/outputs/code-coverage/connected下(文件夹没有的话可新建)

 运行gradlew jacocoTestReport

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)
    executionData = files("$buildDir/outputs/code-coverage/connected/coverage.ec")

    doFirst {
        new File("$buildDir/intermediates/classes/").eachFileRecurse { file ->
            if (file.name.contains('$$')) {
                file.renameTo(file.path.replace('$$', '$'))
            }
        }
    }
}

 

4.report路径:\jscscs\app\build\reports\jacoco\jacocoTestReport

 

通过广播驱动生成ec:

1.注册广播

 public void onReceive(Context context, Intent intent) {
        String url = intent.getStringExtra("url");
        if (Constants.mListener != null) {
            Constants.mListener.onActivityFinished();
            Toast.makeText(context,"notnull", 0).show();
        }else{
            Toast.makeText(context,url, 0).show();
        }
}
 <receiver android:name="test.operationReceiver" android:enabled="true" android:exported="true">
            <intent-filter>
                <action android:name="com.jacs.zhaotang.jscscs.operationReceiver" />
            </intent-filter>
        </receiver>

发送广播:adb shell am broadcast -a com.jacs.zhaotang.jscscs.operationReceiver --es url 'sdsd'

 

 

解析xml:

需要提取的覆盖率类型

    public static final String INSTRUCTION="INSTRUCTION";
    public static final String BRANCH="BRANCH";
    public static final String LINE="LINE";
    public static final String COMPLEXITY="COMPLEXITY";
    public static final String METHOD="METHOD";
    public static final String[] COVERES={INSTRUCTION};

 

// 获取当前节点的所有属性节点
        List<Attribute> list = node.attributes();
        // 遍历属性节点
        for (Attribute attr : list) {
            if (node.getName().equals("counter")) { // 节点等于 counter
                if (needCollect(attr.getText()) || is) { // 当属性等于INSTRUCTION拿出covered                                        // INSTRUCTION                                                                    // 拿出该covered
                    is = true;
                }
                if (attr.getName().equals("covered") && is) {
                    String p=getFristAttributeText(node.getParent());
                    data += node.getParent().getName() + " -  "
                            + p + " ---  "
                            + currentCovered+"-"+attr.getName() + " ----- " + attr.getText()
                            + "  ";
                    is = false;
                    break;
                }

            }

        }

 

获取A和B两次代码的覆盖率

method -  unRegisterObserver ---  INSTRUCTION-covered ----- 14  
method -  getLastLocation ---  INSTRUCTION-covered ----- 18  
method -  setCachedLocation ---  INSTRUCTION-covered ----- 0  
method -  getCachedLocation ---  INSTRUCTION-covered ----- 11  
method -  setCachedLocationTime ---  INSTRUCTION-covered ----- 0  
method -  getCacheLocationTime ---  INSTRUCTION-covered ----- 11  
method -  hasCachedLocation ---  INSTRUCTION-covered ----- 10  
method -  <init> ---  INSTRUCTION-covered ----- 6  
method -  onReceive ---  INSTRUCTION-covered ----- 10  
Spent  785  //消耗时间

打印A/B两次的不同覆盖值
A: class - com/android/browser/AutoLoginLinearLayout --- INSTRUCTION-covered ----- 20 B: class - com/android/browser/AutoLoginLinearLayout --- INSTRUCTION-covered ----- 0

 

posted @ 2017-01-21 21:37  q55091  阅读(1171)  评论(0编辑  收藏  举报