zoukankan      html  css  js  c++  java
  • 安卓app功能或自动化测试覆盖率统计(不用instrumentation启动app)

       一文带你揭秘如何采取非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('$$', '$'))
                }
            }
        }
    }
    

      然后点击

    执行完毕后。

    我们看下实际的效果

    可以看到有测试报告,我们打开看下。

    这是最后的覆盖率测试的统计数据。

            这里的数据呢,只是统计到了全量代码的,还有增量代码覆盖率统计,多个覆盖率文件的不同的如何进行组合。后续的文章会持续分享。

     
  • 相关阅读:
    枚举求解,也叫暴力破解
    多线程之线程的中止
    beginners with the Multithreading programming
    EditBox control Update Problem (A CString type EditBox control in a loop cannot be updated immidiately)
    有关多线程的一些技术问题
    多线程编程基础知识
    VC的若干实用小技巧 (如何干净的删除一个类)
    进入MFC讲坛的前 (Window 程序详细分析)
    Multithreading using MFC in Plain English: Part I
    关于在VS2005环境下无法从源视图切换到设计视图的解决方案
  • 原文地址:https://www.cnblogs.com/leiziv5/p/13772511.html
Copyright © 2011-2022 走看看