<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Jacoco on Trevor's Blog</title><link>http://mings.in/tags/jacoco/</link><description>Recent content in Jacoco on Trevor's Blog</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Thu, 20 Jun 2019 09:00:00 +0800</lastBuildDate><atom:link href="http://mings.in/tags/jacoco/index.xml" rel="self" type="application/rss+xml"/><item><title>使用 Jacoco 检查测试覆盖率</title><link>http://mings.in/posts/enable-jacoco-for-manual-test/</link><pubDate>Thu, 20 Jun 2019 09:00:00 +0800</pubDate><guid>http://mings.in/posts/enable-jacoco-for-manual-test/</guid><description>在手动测试中启用 jacoco 来检查测试覆盖率</description><content:encoded><![CDATA[<p>​    团队中目前还没有自动化测试的覆盖，所以测试 team 想了解下手动测试的覆盖率。于是才有了本片文章的产生。网上有很多文章是利用 Android 的 instrument 测试框架，然后通过命令来启动app来进行测试。而且报告生产的时间点是在启动的 activity 结束以后，在复杂场景下，是没有办法来捕捉到所有页面的函数调用的。</p>
<p>本文中的方案是对一个新的 <code>build type</code> 来重载 Application 代码，只在手动测试时候使用，对原来的代码不会产生任何影响。 希望可以帮到你。本文的实例代码可以到这里下载<a href="https://github.com/trevorwang/jacoco-manual-coverage">trevorwang/jacoco-manual-coverage</a></p>
<ol>
<li>
<p>在你的工程目录的<code>buildscripts</code> 下，新建一个 <code>jacoco.gradle</code> 的文件，添加如下代码</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="n">apply</span> <span class="nl">plugin:</span> <span class="s1">&#39;jacoco&#39;</span>  <span class="c1">// 开启 jacoco
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">jacoco</span> <span class="o">{</span>	
</span></span><span class="line"><span class="cl">    <span class="n">toolVersion</span> <span class="o">=</span> <span class="s2">&#34;0.8.3&#34;</span>  <span class="c1">// 设置 jacoco 版本号
</span></span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 如果使用了 Robolectric 请务必添加如下代码
</span></span></span><span class="line"><span class="cl"><span class="n">tasks</span><span class="o">.</span><span class="na">withType</span><span class="o">(</span><span class="n">Test</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">jacoco</span><span class="o">.</span><span class="na">includeNoLocationClasses</span> <span class="o">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div></li>
<li>
<p>在 app 目录下的 <code>build.gradle</code> 中添加代码,来启用脚本</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="n">apply</span> <span class="nl">from:</span> <span class="s1">&#39;../buildscripts/jacoco.gradle&#39;</span>
</span></span></code></pre></div></li>
<li>
<p>执行 <code>testDebugUnitTest</code> 后，会在<code>app/build/jococo/</code> 下看到 <code>testDebugUnitTest.exec</code>。 记住这个文件 我们会在后面用这个文件来生产报告。</p>
<p><img alt="jacoco-executedata" loading="lazy" src="/images/jacoco-executedata.png"></p>
</li>
<li>
<p>创建 <code>jacoco</code> 任务</p>
<p>Android gradle plugin 会生成不同的 <code>variant</code>, 所以我们要对不用的 <code>variant</code> 生成不用的任务来生产报告。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="n">project</span><span class="o">.</span><span class="na">afterEvaluate</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">android</span><span class="o">.</span><span class="na">applicationVariants</span><span class="o">.</span><span class="na">all</span> <span class="o">{</span> <span class="n">variant</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">def</span> <span class="n">variantName</span> <span class="o">=</span> <span class="n">variant</span><span class="o">.</span><span class="na">name</span>
</span></span><span class="line"><span class="cl">        <span class="kt">def</span> <span class="n">testTaskName</span> <span class="o">=</span> <span class="s2">&#34;test${variantName.capitalize()}UnitTest&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">tasks</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="nl">name:</span> <span class="s2">&#34;${testTaskName}Coverage&#34;</span><span class="o">,</span> <span class="nl">type:</span> <span class="n">JacocoReport</span><span class="o">,</span> <span class="nl">dependsOn:</span> <span class="s2">&#34;$testTaskName&#34;</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">          <span class="c1">// TODO 后面实现
</span></span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>点击<code>Sync Gradle</code> 后，gradle task 会增加两个任务 <code> testDebugUnitTestCoverage</code>, <code>testReleaseUnitTestCoverage</code>，接下来我们增加实现报告生成的任务。</p>
</li>
<li>
<p>使用一下代码替换上一个步骤中的<code>TODO</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"> <span class="n">group</span> <span class="o">=</span> <span class="s2">&#34;Reporting&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">description</span> <span class="o">=</span> <span class="s2">&#34;Generate Jacoco coverage reports for the ${variantName.capitalize()} build.&#34;</span>
</span></span><span class="line"><span class="cl"> 		<span class="c1">// 设置报告格式
</span></span></span><span class="line"><span class="cl">    <span class="n">reports</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">html</span><span class="o">.</span><span class="na">enabled</span> <span class="o">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">        <span class="n">xml</span><span class="o">.</span><span class="na">enabled</span> <span class="o">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	 <span class="c1">// 排除不需要统计的类
</span></span></span><span class="line"><span class="cl">    <span class="kt">def</span> <span class="n">excludes</span> <span class="o">=</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;**/R.class&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;**/R$*.class&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;**/BuildConfig.*&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;**/Manifest*.*&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;**/*Test*.*&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;android/**/*.*&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">      		  <span class="s1">&#39;androidx/**/*.*&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="o">]</span>
</span></span><span class="line"><span class="cl">	  <span class="c1">// Java 类文件
</span></span></span><span class="line"><span class="cl">    <span class="kt">def</span> <span class="n">javaClasses</span> <span class="o">=</span> <span class="n">fileTree</span><span class="o">(</span><span class="nl">dir:</span> <span class="n">variant</span><span class="o">.</span><span class="na">javaCompiler</span><span class="o">.</span><span class="na">destinationDir</span><span class="o">,</span> <span class="nl">excludes:</span> <span class="n">excludes</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">	  <span class="c1">// Kotlin 文件
</span></span></span><span class="line"><span class="cl">    <span class="kt">def</span> <span class="n">kotlinClasses</span> <span class="o">=</span> <span class="n">fileTree</span><span class="o">(</span><span class="nl">dir:</span> <span class="s2">&#34;${buildDir}/tmp/kotlin-classes/${variantName}&#34;</span><span class="o">,</span> <span class="nl">excludes:</span> <span class="n">excludes</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">classDirectories</span> <span class="o">=</span> <span class="n">files</span><span class="o">([</span><span class="n">javaClasses</span><span class="o">,</span> <span class="n">kotlinClasses</span><span class="o">])</span>
</span></span><span class="line"><span class="cl">	  <span class="c1">// 源文件
</span></span></span><span class="line"><span class="cl">    <span class="n">sourceDirectories</span> <span class="o">=</span> <span class="n">files</span><span class="o">([</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;$project.projectDir/src/main/java&#34;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;$project.projectDir/src/${variantName}/java&#34;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;$project.projectDir/src/main/kotlin&#34;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;$project.projectDir/src/${variantName}/kotlin&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="o">])</span>
</span></span><span class="line"><span class="cl">  	<span class="c1">// 最开始我们生成的文件
</span></span></span><span class="line"><span class="cl">    <span class="n">executionData</span> <span class="o">=</span> <span class="n">files</span><span class="o">(</span><span class="s2">&#34;${project.buildDir}/jacoco/${testTaskName}.exec&#34;</span><span class="o">)</span>
</span></span></code></pre></div></li>
<li>
<p>执行一下 <code>testDebugUnitTestCoverage</code> 任务，我们就会在build 目录下看到报告了</p>
<p><img loading="lazy" src="/images/jacoco-reports.png"></p>
<p>经过以上步骤我们完成了一个jacoco 报告的生成过程。</p>
</li>
</ol>
<p>关键步骤来了，如何在打包的app中开启jacoco呢？</p>
<ol start="7">
<li>
<p>新建一个<code>staging</code> 的build type</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl">    <span class="n">buildTypes</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">release</span> <span class="o">{...}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">staging</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">initWith</span><span class="o">(</span><span class="n">debug</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">matchingFallbacks</span> <span class="o">=</span> <span class="o">[</span><span class="s2">&#34;debug&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">            <span class="n">testCoverageEnabled</span> <span class="kc">true</span>    <span class="c1">// 会将jacoco runtime打包至app中
</span></span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span></code></pre></div></li>
<li>
<p>在<code>src</code> 目录下，与 <code>main</code> 通级，新建 <code>staging</code> 目录</p>
</li>
<li>
<p><code>staging</code> 目录下新建 <code>java</code>目录，并在 <code>com.example.staging</code> 包下新建 <code>StagingApp.kt</code>文件，代码如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="line"><span class="cl"><span class="k">package</span> <span class="nn">com.example.staging</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.Manifest</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.app.Activity</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.app.Application</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.os.Bundle</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.os.Environment</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.util.Log</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.widget.Toast</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">androidx.fragment.app.FragmentActivity</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">com.tbruyelle.rxpermissions2.RxPermissions</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">java.io.File</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">java.io.FileOutputStream</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">java.io.IOException</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">StagingApp</span> <span class="p">:</span> <span class="n">Application</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">override</span> <span class="k">fun</span> <span class="nf">onCreate</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">super</span><span class="p">.</span><span class="n">onCreate</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="nc">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s2">&#34;StagingApp&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">registerActivityLifecycleCallbacks</span><span class="p">(</span><span class="k">object</span> <span class="err">: </span><span class="nc">ActivityLifecycleCallbacks</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">var</span> <span class="py">activitySize</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityPaused</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityResumed</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 第一个activity 请求 SD card 目录访问权限
</span></span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">activitySize</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="n">activity</span> <span class="k">as</span><span class="p">?</span> <span class="n">FragmentActivity</span><span class="p">)</span><span class="o">?.</span><span class="n">let</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="k">val</span> <span class="py">rxPerm</span> <span class="p">=</span> <span class="n">RxPermissions</span><span class="p">(</span><span class="k">it</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                        <span class="n">rxPerm</span><span class="p">.</span><span class="n">request</span><span class="p">(</span><span class="nc">Manifest</span><span class="p">.</span><span class="n">permission</span><span class="p">.</span><span class="n">WRITE_EXTERNAL_STORAGE</span><span class="p">).</span><span class="n">subscribe</span><span class="p">({</span> <span class="n">result</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                            <span class="k">if</span> <span class="p">(!</span><span class="n">result</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nc">Toast</span><span class="p">.</span><span class="n">makeText</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                    <span class="k">it</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                    <span class="s2">&#34;You have to grant the permission to save coverage file&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                    <span class="nc">Toast</span><span class="p">.</span><span class="n">LENGTH_SHORT</span>
</span></span><span class="line"><span class="cl">                                <span class="p">).</span><span class="n">show</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                            <span class="p">}</span>
</span></span><span class="line"><span class="cl">                        <span class="p">},</span> <span class="p">{</span> <span class="n">e</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                            <span class="n">e</span><span class="p">.</span><span class="n">printStackTrace</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                        <span class="p">})</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityStarted</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityDestroyed</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">activitySize</span> <span class="o">-=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">activitySize</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                  <span class="c1">//所有activity被销毁后，生产报告文件
</span></span></span><span class="line"><span class="cl">                    <span class="n">generateCoverageReport</span><span class="p">(</span><span class="n">createFile</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivitySaveInstanceState</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?,</span> <span class="n">outState</span><span class="p">:</span> <span class="n">Bundle</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityStopped</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityCreated</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?,</span> <span class="n">savedInstanceState</span><span class="p">:</span> <span class="n">Bundle</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">activitySize</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="k">fun</span> <span class="nf">generateCoverageReport</span><span class="p">(</span><span class="k">file</span><span class="p">:</span> <span class="n">File</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nc">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s2">&#34;generateCoverageReport():</span><span class="si">${file.absolutePath}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">FileOutputStream</span><span class="p">(</span><span class="k">file</span><span class="p">,</span> <span class="k">false</span><span class="p">).</span><span class="n">use</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">val</span> <span class="py">agent</span> <span class="p">=</span> <span class="nc">Class</span><span class="p">.</span><span class="n">forName</span><span class="p">(</span><span class="s2">&#34;org.jacoco.agent.rt.RT&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="n">getMethod</span><span class="p">(</span><span class="s2">&#34;getAgent&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="n">invoke</span><span class="p">(</span><span class="k">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="nc">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="n">agent</span><span class="p">.</span><span class="n">toString</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">            <span class="k">it</span><span class="p">.</span><span class="n">write</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">agent</span><span class="p">.</span><span class="n">javaClass</span><span class="p">.</span><span class="n">getMethod</span><span class="p">(</span><span class="s2">&#34;getExecutionData&#34;</span><span class="p">,</span> <span class="n">Boolean</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">javaPrimitiveType</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="p">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">agent</span><span class="p">,</span> <span class="k">false</span><span class="p">)</span> <span class="k">as</span> <span class="n">ByteArray</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">fun</span> <span class="nf">createFile</span><span class="p">():</span> <span class="n">File</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// SD card 下面
</span></span></span><span class="line"><span class="cl">        <span class="k">val</span> <span class="py">file</span> <span class="p">=</span> <span class="n">File</span><span class="p">(</span><span class="nc">Environment</span><span class="p">.</span><span class="n">getExternalStorageDirectory</span><span class="p">(),</span> <span class="s2">&#34;jacoco/</span><span class="si">$DEFAULT</span><span class="s2">_COVERAGE_FILE_PATH&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(!</span><span class="k">file</span><span class="p">.</span><span class="n">exists</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">file</span><span class="p">.</span><span class="n">parentFile</span><span class="o">?.</span><span class="n">mkdirs</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="k">file</span><span class="p">.</span><span class="n">createNewFile</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">e</span><span class="p">:</span> <span class="n">IOException</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nc">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s2">&#34;异常 : </span><span class="si">$e</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">e</span><span class="p">.</span><span class="n">printStackTrace</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">file</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">companion</span> <span class="k">object</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">const</span> <span class="k">val</span> <span class="py">DEFAULT</span><span class="n">_COVERAGE_FILE_PATH</span> <span class="p">=</span> <span class="s2">&#34;jacoco-coverage.ec&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">const</span> <span class="k">val</span> <span class="py">TAG</span> <span class="p">=</span> <span class="s2">&#34;StagingApp&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>
<p><code>staging</code> 目录中新建一个 <code>AndroidManifest.xml</code> 文件，内容如下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;manifest</span>
</span></span><span class="line"><span class="cl">        <span class="na">xmlns:android=</span><span class="s">&#34;http://schemas.android.com/apk/res/android&#34;</span> <span class="na">package=</span><span class="s">&#34;com.example.jacocomanual&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;uses-permission</span> <span class="na">android:name=</span><span class="s">&#34;android.permission.WRITE_EXTERNAL_STORAGE&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;application</span> <span class="na">android:name=</span><span class="s">&#34;com.example.staging.StagingApp&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/manifest&gt;</span>
</span></span></code></pre></div><p>最后效果如下：</p>
<p><img alt="jacoco-staging-build-type" loading="lazy" src="/images/jacoco-staging-build-type.png"></p>
</li>
<li>
<p>IDE <code>Build Variants</code> 下选择 <code>staging</code></p>
<p><img alt="jacoco-build-variants" loading="lazy" src="/images/jacoco-build-variants.png"></p>
</li>
<li>
<p>运行app并安装到设备或者模拟器，操作一下，然后按返回键关闭所有的页面，这时候会在 SD 卡目录下的生成  <code>jacoco/jacoco-coverage.ec</code> 文件。</p>
<p><img alt="jacoco-sd-location" loading="lazy" src="/images/jacoco-sd-location.png"></p>
</li>
<li>
<p>复制 <code>jacoco-coverage.ec</code> 文件到项目根目录下的 <code>jacoco</code> 文件夹</p>
</li>
<li>
<p>我们来修改jacoco的任务来生成最后的报告</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl">            <span class="c1">// 最开始我们生成的文件
</span></span></span><span class="line"><span class="cl">            <span class="n">executionData</span> <span class="o">=</span> <span class="n">files</span><span class="o">([</span>
</span></span><span class="line"><span class="cl">                    <span class="s2">&#34;${project.buildDir}/jacoco/${testTaskName}.exec&#34;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">                    <span class="s2">&#34;${rootDir}/jacoco/jacoco-coverage.ec&#34;</span>   <span class="c1">//增加一个数据源
</span></span></span><span class="line"><span class="cl">            <span class="o">])</span>
</span></span></code></pre></div></li>
<li>
<p>运行 <code>testStagingUnitTest</code> 这样就可以看到报告了</p>
<p><img alt="jacoco-report-html" loading="lazy" src="/images/jacoco-report-html.png"></p>
</li>
</ol>
<p>真香！~~</p>
]]></content:encoded></item></channel></rss>