Reference: https: //docs.gradle.org/current/dsl/org.gradle.testing.jacoco.tasks.JacocoCoverageVerification.html
gradle Library Download: https: //maven.aliyun.com/mvn/view
Case Reference Source: https: //www.jianshu.com/p/1a4a81f09526
https://www.jianshu.com/p/1a4a81f09526
Other: https: //testerhome.com/topics/8329
These days toss for a long time, is now the main case are based gradle3.1.3 version, I do not want to use the old version, check some information he changed under the code, it can be used.
antecedent:
Before you can use Android heard jacoco + monkey do code coverage test, previously only had a spring unit test coverage jacoco the demo, thought Android features and jacoco can be united, very busy these days to engage in a bit.
Ready to work:
Have Android project source code, without modifying the core code of the main project, but need to write some code jacoco, mainly using instrument recording code coverage at the end of acitivity;
The specific content of two points:
A, Android project unit test code coverage:
Use AndroidStudio own task to view the current unit test coverage AndroidTest folder under the circumstances
Edit build.gradle
{Android ... defaultConfig { ... testInstrumentationRunnerArguments clearPackageData: 'to true' // perform cleanup test instrumentation cache } buildTypes { Debug { testCoverageEnabled to true = / ** Open coverage statistics switch * / } }
Installing debug packages
AndroidTest execution coverage tests and output reports
Execution log is this:
Here is a fragment removal AndroidTest execution unit testing, by sending commands to the phone adb instrument, a test is performed, obtaining coverage data from the phone down and down:
Executing tasks: [createDebugAndroidTestCoverageReport] ... > Task :app:connectedDebugAndroidTest ... 05:40:39 V/ddms: execute: running am instrument -w -r -e coverageFile /data/data/com.patech.testApp/coverage.ec -e coverage true -e clearPackageData true com.patech.testApp.test/androidx.test.runner.AndroidJUnitRunner ... 05:40:41 V/InstrumentationResultParser: com.patech.testApp.EspressoTest: ... 05:40:58 V/InstrumentationResultParser: Time: 17.669 05:40:58 V/InstrumentationResultParser: 05:40:58 V/InstrumentationResultParser: OK (5 tests) 05:40:58 V/InstrumentationResultParser: 05:40:58 V/InstrumentationResultParser: 05:40:58 V/InstrumentationResultParser: Generated code coverage data to /data/data/com.patech.testApp/coverage.ec 05:40:58 V/InstrumentationResultParser: INSTRUMENTATION_CODE: -1 ... 05:40:59 I/XmlResultReporter: XML test result file generated at D:\androidStudio\MyApplication\app\build\outputs\androidTest-results\connected\TEST-VOG-AL10 - 9-app-.xml. Total tests 5, passed 5, 05:40:59 V/ddms: execute 'am instrument -w -r -e coverageFile /data/data/com.patech.testApp/coverage.ec -e coverage true -e clearPackageData true com.patech.testApp.test/androidx.test.runner.AndroidJUnitRunner' on 'APH0219430006864' : EOF hit. Read: -1 ... 05:40:59 D/com.patech.testApp.coverage.ec: Downloading com.patech.testApp.coverage.ec from device 'APH0219430006864' ...
View reports in the build after finishing execution details
II. Jacoco + instrument to write code, after performing functional tests, ec file generated locally, spread to the pc-parsed into html format to view the implementation of the code coverage function test operation
1. Preparation of FinishListener Interface
public interface FinishListener { void onActivityFinished(); void dumpIntermediateCoverage(String filePath); }
Write jacocoInstrumentation methods to achieve above this interface, the online copy to achieve a generation after the implementation of complete coverage of file and save it locally to your phone:
package com.patech.test; import android.app.Activity; import android.app.Instrumentation; import android.content.Intent; import android.os.Bundle; import android.os.Looper; import android.util.Log; import com.patech.testApp.InstrumentedActivity; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; 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; //LOGD 调试用布尔 private static final boolean LOGD = true; private boolean mCoverage = true; private String mCoverageFilePath; public JacocoInstrumentation(){ } @Override public void onCreate(Bundle arguments) { Log.d(TAG, "onCreate(" + arguments + ")"); super.onCreate(arguments); //DEFAULT_COVERAGE_FILE_PATH = getContext().getFilesDir().getPath() + "/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) { mCoverageFilePath = arguments.getString("coverageFile"); } mIntent = new Intent(getTargetContext(), InstrumentedActivity.class); mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); start(); } 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 String getCoverageFilePath() { if (mCoverageFilePath == null) { return DEFAULT_COVERAGE_FILE_PATH; }else { return mCoverageFilePath; } } 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 (FileNotFoundException e) { Log.d(TAG, e.toString(), e); } catch (IOException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } public void UsegenerateCoverageReport() { generateCoverageReport(); } private boolean setCoverageFilePath(String filePath){ if (filePath != null && filePath.length() > 0) { mCoverageFilePath = filePath; } 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_IDENTIFIER,"\nError: " + msg); } @Override public void onActivityFinished() { if (LOGD) { Log.d(TAG,"onActivityFinished()"); } finish(Activity.RESULT_OK,mResults); } @Override public void dumpIntermediateCoverage(String filePath) { 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); } } }
2. Modify AndroidManifest.xml file, add the permission to read and write the phone, as well as instrument settings, and application of the tag label at the same level:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Jacoco权限 --> <uses-permission android:name="android.permission.USE_CREDENTIALS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <instrumentation android:name="com.patech.test.JacocoInstrumentation" android:handleProfiling="true" android:label="CoverageInstrumentation" android:targetPackage="com.patech.testApp" />
3. Write jacoco.gradle, for parsing ec, or converted into other formats html report:
apply plugin: 'jacoco' //https://docs.gradle.org/current/userguide/jacoco_plugin.html jacoco { toolVersion = "0.8.4" } task jacocoTestReport(type: JacocoReport) { group = "Reporting" description = "Generate Jacoco coverage reports after running tests." def debugTree = fileTree(dir: "${buildDir}/intermediates/javac/debug", // includes: ["**/*Presenter.*"], excludes: ['**/R*.class', '**/*$InjectAdapter.class', '**/*$ModuleAdapter.class', '**/*$ViewInjector*.class' ]) // specified class folder that contains the rules of the class and the class of the exclusionary rule, where we generate test reports for all classes Presenter def coverageSourceDirs = "${project.projectDir}/src/main/java" //指定源码目录 def reportDirs="$buildDir/outputs/reports/jacoco/jacocoTestReport" reports { xml.enabled = true html.enabled = true } // destinationFile=file(reportDirs) classDirectories = files(debugTree) sourceDirectories = files(coverageSourceDirs) executionData = files("$buildDir/outputs/code-coverage/connected/coverage.ec") }
4. Connect the phone, the statement after the implementation of adb install apk, open the application by jacoco:
adb shell am instrument -w -r com.patech.testApp / com.patech.testcoverage.test.JacocoInstrumentation
5. You can start to do a functional test on the phone, ec export file after the test is completed:
adb pull mnt/sdcard/coverage.ec C:\Users\user\Desktop\testReport\jacoco
6. ec file in build / outputs / code-coverage / connected at
The task execution jacocoTestReport
View resolved in build / reports / jacoco / jacocoTestReport report
View the report: