2015-06-22 17 views
6

stiamo costruendo un'app per Android che viene testata utilizzando Appium. Ora mi piacerebbe vedere la copertura di test dei nostri test di Appium. Penso che questo sia possibile, perché Jacoco supporta la strumentazione offline (http://www.eclemma.org/jacoco/trunk/doc/offline.html).Android Gradle Jacoco: strumentazione offline per test di integrazione

E anche la documentazione del plugin jacoco Gradle dice:

Mentre tutte le attività di tipo di test sono automaticamente migliorati per fornire informazioni sulla copertura, quando è stato applicato il plugin Java, qualsiasi compito che implementa JavaForkOptions può essere migliorata dal plugin JaCoCo. Cioè, qualsiasi attività che forca i processi Java può essere utilizzata per generare informazioni sulla copertura.

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

Ma come devo scrivere il build.gradle quindi il nostro sapore accettazione di debug è strumentato e il file exec viene scritto nel Smartphone quando i test vengono eseguiti Appium o anche casi di test manuali sono eseguiti? Perché allora posso estrarre il file exec e inviarlo in modo da SonarQube per ulteriori analisi.

Grazie Ben

risposta

1

Finalmente sono riuscito a farlo funzionare e voglio condividere la soluzione con voi:

abilitare la strumentazione per il vostro buildtype e configurare SonarQube conseguenza esempio

... 
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 
    } 
} 

aggiungere il seguente al vostro AndroidManifest.xml

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

CoverageDataDumper dovrebbe essere simile che:

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); 
     } 
    } 
} 

quindi eseguire il test case Appium con l'applicazione sapore accettazione (con le classi di strumentazione). Prima di chiamare "Reset App" o "Chiudi applicazione" fare in modo di chiamare i seguenti metodi (solo un progetto, ma penso che si ottiene l'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 potrebbe essere per esempio:/sdcard/Android /data/org.example.acceptance/files/coverage.ec

Ora i dati di Jacoco vengono scritti sullo smartphone. Quindi dobbiamo scaricare quel file. È possibile utilizzare

appiumDriver.pullFile(logFilePath); 

Ora è necessario copiare il file "jacoco-it.exec" (che dovrebbe essere sempre allegato quando si tira il file) in build/jacoco/jacoco-it.exec vedere Gradle .build sopra e gestiscono

gradlew sonarRunner 

Nel SonarQube aggiungere l'integrazione di prova copertura widget e si dovrebbe vedere ora alcuni valori ...

Purtroppo copertura del codice non funziona se si utilizza retrolambda (come noi fare).Retrolambda genererà classi anonime che non fanno parte dei file di origine, quindi SonarQube non può corrispondere correttamente a loro e visualizza una copertura del codice molto più bassa di quanto non sia in realtà. Se qualcuno trova una soluzione per questo, sarei molto felice :-)

+0

mi sto perdendo qualcosa, o stai usando sia Emma e jacoco qui? Ho un problema simile, quindi questa risposta è molto interessante, ma non riesco a capire. – Vish

0

Ho risolto questo problema aggiungendo il ricevitore di trasmissione all'applicazione testata! (È possibile aggiungere il ricevitore solo per cartella di debug causare alcun bisogno per esistere in principale fonte)

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()); 
    } 
} 
} 

In manefist aggiuntivo (è possibile aggiungere questa cartella di debug in modo che non esisterà nel principale fonte)

<?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> 

Nel build.gradle dell'applicazione ho aggiunto

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 

     } 
    } 

costruire la vostra applicazione come debug, che installare ed eseguirlo.

invio broadcast attraverso ADB "adb shell pm trasmissione -a com.example.action" per creare una copertura coverage.exec tirare dal dispositivo - adb pull /mnt/sdcard/coverage.exec

dopo aver eseguito questo è necessario creare la copertura dal file

** 
* 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') 
} 

questo compito è un modo per creare file di copertura in coverageSourceDirs aggiungere tutte le sedi del codice sorgente applicaiton, ti sapere quale codice per prendere e creare a base di copertura su di loro executionData è la posizione dove hai inserito la copertura.exec hai estratto dal dispositivo

Esegui l'attività i file verranno creati per html e xml puoi anche aggiungere csv (notare che verrà creato nella cartella di build dell'applicazione)!

bisogno di sapere, è necessario eseguire l'operazione contro lo stesso codice che avete costruito la vostra versione di debug dell'applicazione

Problemi correlati