Android Gradle Jacoco: instrumentación offline para pruebas de integración

Estamos construyendo una aplicación para Android que se prueba con Appium. Ahora me gustaría ver la cobertura de prueba de nuestras pruebas de Appium. Creo que esto es posible, porque Jacoco apoya la instrumentación fuera de línea ( http://www.eclemma.org/jacoco/trunk/doc/offline.html ).

Y hasta la documentación del plugin jacaro gradle dice:

Si bien todas las tareas del tipo Test se mejoran automáticamente para proporcionar información de cobertura cuando se ha aplicado el complemento java, cualquier tarea que implemente JavaForkOptions puede ser mejorada por el complemento JaCoCo. Es decir, cualquier tarea que forks procesos Java se puede utilizar para generar información de cobertura.

Consulte https://docs.gradle.org/current/userguide/jacoco_plugin.html

¿Pero cómo tengo que escribir el build.gradle así que nuestro sabor de la depuración de la aceptación es instrumentado y el archivo del exec se escribe en el Smartphone cuando las pruebas de Appium se ejecutan o incluso los casos de prueba manuales se ejecutan? Porque entonces puedo extraer el archivo exec y enviarlo de modo que SonarQube para su posterior análisis.

Gracias Ben

    2 Solutions collect form web for “Android Gradle Jacoco: instrumentación offline para pruebas de integración”

    Finalmente lo conseguí para que funcionara y quiero compartir la solución contigo:

    Habilitar instrumentación para su buildType y configurar SonarQube en consecuencia eg

    ... apply plugin: 'jacoco' ... android { ... productFlavors { acceptance { applicationId packageName + ".acceptance" buildTypes { debug { testCoverageEnabled true } } } } } sonarRunner { sonarProperties { property "sonar.host.url", "..." property "sonar.jdbc.url", sonarDatabaseUrl property "sonar.jdbc.driverClassName", sonarDatabaseDriverClassName property "sonar.jdbc.username", sonarDatabaseUsername property "sonar.jdbc.password", sonarDatabasePassword property "sonar.sourceEncoding", "UTF-8" property "sonar.sources", "src/main" property "sonar.tests", "src/test" property "sonar.inclusions", "**/*.java,**/*.xml" property "sonar.import_unknown_files", "true" property "sonar.java.binaries", "build/intermediates/classes/acceptance/debug" property "sonar.junit.reportsPath", "build/test-results/acceptanceDebug" property "sonar.android.lint.report", "build/outputs/lint-results.xml" property "sonar.java.coveragePlugin", "jacoco" property "sonar.jacoco.reportPath", "build/jacoco/testAcceptanceDebugUnitTest.exec" // see steps below on how to get that file: property "sonar.jacoco.itReportPath", "build/jacoco/jacoco-it.exec" property "sonar.projectKey", projectKey property "sonar.projectName", projectName property "sonar.projectVersion", appVersionName } } 

    Añada lo siguiente a su AndroidManifest.xml

     <receiver android:name=".util.CoverageDataDumper" tools:ignore="ExportedReceiver"> <intent-filter> <action android:name="org.example.DUMP_COVERAGE_DATA"/> </intent-filter> </receiver> 

    CoverageDataDumper debería ser así:

     public class CoverageDataDumper extends BroadcastReceiver { private static final Logger LOG = LoggerFactory.getLogger( CoverageDataDumper.class ); @Override public void onReceive( Context context, Intent intent ) { try { Class .forName( "com.vladium.emma.rt.RT" ) .getMethod( "dumpCoverageData", File.class, boolean.class, boolean.class ) .invoke( null, new File( App.getContext().getExternalFilesDir( null ) + "/coverage.ec" ), true, // merge false // stopDataCollection ); } catch ( Exception e ) { LOG.error( "Error when writing coverage data", e ); } } } 

    A continuación, ejecute los casos de prueba de Appium con la aplicación de sabor de aceptación (con clases instrumentadas). Antes de llamar a "Restablecer aplicación" o "Cerrar aplicación" asegúrese de llamar a los siguientes métodos (sólo un borrador, pero creo que tienes la idea):

     // intent is "org.example.DUMP_COVERAGE_DATA" public void endTestCoverage( String intent ) { if ( driver instanceof AndroidDriver ) { ((AndroidDriver) driver).endTestCoverage( intent, "" ); } } public void pullCoverageData( String outputPath ) { String coverageFilePath = (String) appiumDriver.getCapabilities().getCapability( "coverageFilePath" ); if ( coverageFilePath != null ) { byte[] log = appiumDriver.pullFile( coverageFilePath ); MobileAppLog.writeLog( new File( outputPath ), log ); } else { throw new AppiumLibraryNonFatalException( "Tried to pull the coverage data, but the coverageFilePath wasn't specified." ); } } 

    OutputPath podría ser por ejemplo: /sdcard/Android/data/org.example.acceptance/files/coverage.ec

    Ahora los datos Jacoco se escriben en el Smartphone. Luego necesitamos descargar ese archivo. Puedes usar

     appiumDriver.pullFile( logFilePath ); 

    Ahora debes copiar el archivo "jacoco-it.exec" (que siempre se debe anexar al extraer el archivo) en build / jacoco / jacoco-it.exec vea gradle.build arriba y ejecuta

     gradlew sonarRunner 

    En SonarQube agregue el Widget de Cobertura de la Prueba de Integración y verá ahora algunos valores …

    Lamentablemente, la cobertura de código no funcionará si está usando retrolambda (como nosotros). Retrolambda generará clases anónimas que no forman parte de los archivos fuente, por lo que SonarQube no puede coincidir correctamente y muestra una cobertura de código mucho menor de lo que realmente es. Si alguien encuentra una solución para eso, yo sería muy feliz 🙂

    He resuelto este problema mediante la adición de receptor de difusión a la aplicación que prueba! (Solo puede agregar el receptor a la carpeta de depuración porque no hace falta que exista en la fuente principal)

      public class CoverageReceiver extends BroadcastReceiver { private static final String EXEC_FILE_PATH = "/mnt/sdcard/coverage.exec"; private static final String TAG = "CoverageJacoco"; private static final String BROADCAST_RECEIVED_MESSAGE = "EndJacocoBroadcast broadcast received!"; private static final String EMMA_CLASS = "com.vladium.emma.rt.RT"; private static final String EMMA_DUMP_METHOD = "dumpCoverageData"; @Override public void onReceive(Context context, Intent intent) { try { Log.d(TAG, BROADCAST_RECEIVED_MESSAGE); Class.forName(EMMA_CLASS) .getMethod(EMMA_DUMP_METHOD, File.class, boolean.class, boolean.class) .invoke(null, new File(EXEC_FILE_PATH), true, false); } catch (Exception e) { Log.d(TAG, e.getMessage()); } } } 

    En manefist add (puedes agregar esta carpeta de depuración para que no exista en la fuente principal)

      <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" > <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application> <receiver android:name=".CoverageReceiver"> <intent-filter> <action android:name="com.example.action" /> </intent-filter> </receiver> </application> 

    En el build.gradle de la aplicación he añadido

     apply plugin: 'jacoco' jacoco { toolVersion = "0.7.4+" } model { android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.example.app" minSdkVersion.apiLevel 23 targetSdkVersion.apiLevel 23 versionCode 12 versionName "1.11" } buildTypes { debug { testCoverageEnabled true } } 

    Usted construye su aplicación como depuración, que la instala y la ejecuta.

    Enviar broadcast a través de ADB "adb shell am broadcast -a com.example.action" para crear coverage.exec pull coverage desde el dispositivo – adb pull /mnt/sdcard/coverage.exec

    Después de ejecutar esto, necesita crear la cobertura desde el archivo

      ** * This task is used to create a code coverage report via the Jcoco tool. */ task jacocoTestReport(type: JacocoReport) { def coverageSourceDirs = [ 'src/main/java', ] group = "Reporting" description = "Generates Jacoco coverage reports" reports { csv.enabled false xml{ enabled = true destination "${buildDir}/jacoco/jacoco.xml" } html{ enabled true destination "${buildDir}/jacocoHtml" } } classDirectories = fileTree( dir: 'build/intermediates/classes', excludes: ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Activity*.*', '**/*Fragment*.*' ] ) sourceDirectories = files(coverageSourceDirs) executionData = files('build/coverage.exec') } 

    Esta tarea es una forma de crear archivos de cobertura en coverageSourceDirs añadir todas las ubicaciones de su código fuente applicaiton, por lo que sabrá qué código tomar y crear la cobertura sobre la base de ellos executionData es la ubicación donde se pone el coverage.exec que ha sacado de la dispositivo

    Ejecute la tarea que los archivos creados para html y xml también puede agregar csv (nota que se creará en la carpeta de compilación de la aplicación)!

    Necesidad de saber, debe ejecutar la tarea contra el mismo código que ha creado la versión de depuración de la aplicación

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.