JaCoCo测试代码覆盖率

一.背景介绍
在产品客户端的测试过程中,新功能测试以及回归测试在手工测试的情况下,即便是测试用例再详尽,难免也可能会有疏漏之处。故使用代码覆盖率工具Jacoco作为手工测试代码覆盖率的统计.

解决的问题: 通过查看测试代码覆盖率客观数据,来进一步完善业务测试场景,完善测试用例.

二.使用方法
1. 由于Android本身集成了Jacoco工具,故在其他工程如果需要生成代码测试覆盖率,则仅需要添加如下配置

buildTypes {
    debug {
        testCoverageEnabled true
        ... ...
    }
}

2. 如果针对工程添加配置,在build apk时出现死循环锁( Waiting to acquire shared lock on daemon addresses registry),针对此种情况,需要增加如下配置:

configurations.all {
    resolutionStrategy {
        force 'org.jacoco:org.jacoco.report:0.7.4.201502262128'
        force 'org.jacoco:org.jacoco.core:0.7.4.201502262128'
    }
}

app/build.gradle 增加配置(仅仅针对build不成功工程)

3. project/app/build.gradle 增加配置,此配置用于生成代码覆盖率报告

/*Java Code Coverage*/
apply plugin: 'jacoco'
 
def coverageSourceDirs = [
        '../src/com/i/coverage'
]
 
task jacocoTestReport(type: JacocoReport) {
    group = "Reporting"
    description = "Generate Jacoco coverage reports"
    additionalSourceDirs = files(coverageSourceDirs)
    sourceDirectories = files(coverageSourceDirs)
 
    reports {
        xml.enabled = true
        html.enabled = true
    }
 
    classDirectories = fileTree(
            dir: './build/intermediates/classes/normal/debug',
            excludes: ['**/R.class', '**/R$*.class', '**/*$ViewInjector*.*', '**/BuildConfig.*', '**/Manifest*.*']
    )
 
    executionData = files('./build/outputs/code-coverage/connected/flavors/NORMAL/coverage.ec')
}

(1) 生成测试报告需要添加插件jacoco
(2) 针对build不成功工程其jacoco版本必须和2步骤对应,否则会造成报告不能读
(3) coverageSourceDirs为测试代码路径,executionData为生成测试报告的数据源路径

4. 工程配置以及测试工具apk
(1) 测试工程添加广播接收者

 <receiver android:name="test.JacocoReceiver">
      <intent-filter>
          <action android:name="jacoco.test.action.START"/>
          <action android:name="jacoco.test.action.CLEAR"/>
          <action android:name="jacoco.test.action.END"/>
      </intent-filter>
</receiver>

public class JacocoReceiver extends BroadcastReceiver {
    private static String TAG = "JacocoReceiver:";
    private final static String ACTION_START = "jacoco.test.action.START";
    private final static String ACTION_END = "jacoco.test.action.END";
    private final static String ACTION_CLEAR = "jacoco.test.action.CLEAR";
    private static String mCoverageFilePath;
    private Context mContext;

    @Override
    public void onReceive(Context context, Intent intent) {
        mContext = context;
        if (intent != null) {
            String aciton = intent.getAction();
            if (ACTION_START.equals(aciton)) {
                createFile();
            } else if (ACTION_END.equals(aciton)) {
                generateCoverageReport();
            } else if (ACTION_CLEAR.equals(aciton)) {
                deleteCoverageReport();
            }
        }

    }

    private void createFile() {
        mCoverageFilePath = mContext.getFilesDir().getPath() + File.separator + "coverage.ec";
        File file = new File(mCoverageFilePath);
        if (!file.exists()) {
            try {
                file.createNewFile();
                Log.i(TAG, "create report");
            } catch (IOException e) {
                Log.e(TAG, "IOException", e);
            }
        }
    }

    private void generateCoverageReport() {
        Log.i(TAG, "generateCoverageReport():" + mCoverageFilePath);
        OutputStream out = null;
        try {
            out = new FileOutputStream(mCoverageFilePath, 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.e(TAG, "Exception", e);
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    Log.e(TAG, "IOException", e);
                }
            }
            Log.i(TAG, "jacoco end");
        }
    }
    
    private void deleteCoverageReport() {
        if (!TextUtils.isEmpty(mCoverageFilePath)) {
            File file = new File(mCoverageFilePath);
            if (file != null && file.exists() && file.isFile()) {
                file.delete();
                Log.i(TAG, "delete report");
            }
        }
    }
}

(2) 测试工具发送广播,然后手动操作测试

public class MainActivity extends Activity implements View.OnClickListener {
    private final static String ACTION_START = "jacoco.test.action.START";
    private final static String ACTION_END = "jacoco.test.action.END";
    private final static String ACTION_CLEAR = "jacoco.test.action.CLEAR";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        findViewById(R.id.start).setOnClickListener(this);
        findViewById(R.id.end).setOnClickListener(this);
        findViewById(R.id.clear).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start:
                Intent intent = new Intent(ACTION_START);
                sendBroadcast(intent);
                Toast.makeText(this, "start listener coverage",1).show();
                break;
            case R.id.end:
                Intent intentEnd = new Intent(ACTION_END);
                sendBroadcast(intentEnd);
                Toast.makeText(this, "create coverage report end",1).show();
                break;
            case R.id.clear:
                Intent intentClear = new Intent(ACTION_CLEAR);
                sendBroadcast(intentClear);
                Toast.makeText(this, "clear coverage report success",1).show();
                break;
            default:
                break;
        }

    }
}

(1) 测试工具用于生成测试代码数据源,在测试代码覆盖率时需要使用,生成数据源,路径/data/data/{packagename}/files/coverage.ec;
(2) 生成测试代码覆盖率报告时,需要拷贝该数据源到{projectname}/app/build/outputs/code-coverage/connected/flavors/NORMAL/coverage.ec
(3) 执行步骤3的task jacocoTestReport , 则在{projectname}/app/build/reports/jacoco/jacocoTestReport/html 生成测试报告,可针对性的查看.

三.报告样例
在这里插入图片描述
四.后续
以上是全量代码覆盖率的生成,适用于大改版的情况下去做全面的测试覆盖;
在日常的小版本迭代过程中,适合使用差量代码覆盖率的使用.
//TODO 差量代码覆盖率 https://testerhome.com/topics/5823

<think>好的,我现在需要帮助用户了解如何通过终端下载JaCoCo并使用它来测试代码覆盖率。首先,我要确认用户的需求。他们可能是在进行Java项目的代码覆盖率测试,希望使用JaCoCo工具,但不太清楚具体的步骤。用户提到的是通过终端操作,所以可能需要使用命令行或者构建工具如Maven或Gradle。 首先,我应该回忆JaCoCo的官方文档和常见使用方法。JaCoCo通常作为构建工具的插件集成,比如Maven或Gradle,用户可能需要在项目的构建配置文件中添加相应的插件配置。用户可能没有使用过这些工具,或者不太清楚如何在终端中执行相关命令。 接下来,下载JaCoCo的部分。用户可能误解了需要单独下载JaCoCo,因为通常它是通过构建工具的依赖管理自动下载的。比如在Maven中,添加插件配置后,运行mvn test时会自动下载所需的依赖,包括JaCoCo。但用户也可能希望手动下载JaCoCo的二进制包,比如从官网或GitHub Release页面获取。这时候需要指导他们使用curl或wget命令在终端下载。 然后是配置部分。如果用户使用的是Maven,需要在pom.xml中添加JaCoCo插件的配置;如果是Gradle,则是在build.gradle中添加相应的插件和配置。这部分需要分步骤说明,确保用户能正确修改配置文件。 生成测试报告方面,用户需要知道如何执行测试命令,比如mvn test或gradle test,然后JaCoCo会自动生成覆盖率报告。报告的位置一般在target/site/jacoco目录下,用户可以用浏览器打开查看。 另外,用户提到的代码覆盖率测试的高可靠性项目可能需要更严格的配置,比如设置覆盖率阈值,确保测试覆盖率达到一定标准。这部分可以引用参考内容中的引用[4],强调高可靠性项目需要代码覆盖率测试,并且可能需要设置覆盖率限制。 还需要注意用户的问题中可能存在的误区,比如直接下载JaCoCo的jar包而不是通过构建工具管理,这时候要说明两种方法的区别和适用场景。同时,确保提供的命令和步骤在常见的Linux/macOS终端中有效,避免平台相关的问题。 最后,生成相关问题,帮助用户进一步了解JaCoCo的高级用法、与其他工具的集成、阈值配置以及如何解释报告结果等。这些问题应该覆盖用户可能遇到的后续疑问,提升帮助的全面性。</think>[用户可见层] ### 如何通过终端下载并使用JaCoCo测试代码覆盖率 #### 步骤1:通过终端下载JaCoCo JaCoCo可以通过两种方式获取: 1. **通过构建工具自动下载(推荐)** 在Maven或Gradle项目中添加JaCoCo插件配置后,构建工具会自动下载依赖。例如在Maven的`pom.xml`中添加: ```xml <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.10</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> </executions> </plugin> ``` 运行`mvn test`时会自动下载JaCoCo[^4]。 2. **手动下载(适用于非Maven/Gradle项目)** 使用`curl`或`wget`从官网下载: ```bash curl -L -O https://github.com/jacoco/jacoco/releases/download/v0.8.10/jacoco-0.8.10.zip unzip jacoco-0.8.10.zip ``` --- #### 步骤2:配置代码覆盖率测试 以Maven项目为例: 1. **确保插件配置正确**(如步骤1所示) 2. **运行测试并生成报告**: ```bash mvn clean test ``` JaCoCo会在`target/jacoco.exec`生成覆盖率数据,随后生成HTML报告到`target/site/jacoco/index.html`。 --- #### 步骤3:查看覆盖率报告 通过终端命令打开HTML报告: ```bash open target/site/jacoco/index.html # macOS xdg-open target/site/jacoco/index.html # Linux ``` 报告会展示类/方法/行覆盖率等指标,例如: $$ \text{行覆盖率} = \frac{\text{覆盖的行数}}{\text{总行数}} \times 100\% $$ --- #### 高级配置(可选) 1. **设置覆盖率阈值** 在`pom.xml`中添加规则限制,确保覆盖率达标: ```xml <configuration> <rules> <rule> <element>CLASS</element> <limits> <limit> <counter>LINE</counter> <value>COVEREDRATIO</value> <minimum>0.80</minimum> </limit> </limits> </rule> </rules> </configuration> ``` 未达标时构建会失败,符合高可靠性项目要求[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值