2015-06-22 10 views
6

Wir bauen eine Android App, die mit Appium getestet wird. Jetzt würde ich gerne die Testabdeckung unserer Appium-Tests sehen. Ich denke, das ist möglich, weil Jacoco Offline-Instrumentierung unterstützt (http://www.eclemma.org/jacoco/trunk/doc/offline.html).Android Gradle Jacoco: Offline-Instrumentierung für Integrationstests

Und auch die Dokumentation der jacoco gradle Plugin sagt:

Während alle Aufgaben der Art der Prüfung werden automatisch erweitert, um Coverage-Informationen bereitzustellen, wenn die Java-Plugin angewandt wurde, dass jede Aufgabe JavaForkOptions implementiert verbessert werden kann durch das JaCoCo-Plugin. Das heißt, jede Aufgabe, die Java-Forks bearbeitet, kann zum Generieren von Coverage-Informationen verwendet werden.

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

Aber wie kann ich die build.gradle zu schreiben haben so unsere Annahme Debug-Geschmack instrumentiert ist und die exec-Datei auf dem Smartphone geschrieben wird, wenn die Appium Tests ausgeführt werden, oder auch manuelle Testfälle werden ausgeführt? Da kann ich dann die exec-Datei extrahieren und sie so SonarQube zur weiteren Analyse schicken.

Dank Ben

Antwort

1

Schließlich schaffte ich es, um es zu arbeiten und ich möchte mit Ihnen die Lösung teilen:

Instrumentierung für Ihre buildType aktivieren und konfigurieren Sonarqube entsprechend z.B.

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

fügen Sie folgendes zu Ihrem AndroidManifest.xml

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

CoverageDataDumper so aussehen sollte:

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

Dann führen Sie Ihre Appium Testfälle mit der Annahme Geschmack app (mit instrumentierten Klassen). Bevor Sie anrufen „App Reset“ oder „Anwendung schließen“ stellen Sie sicher, die folgenden Methoden aufrufen (nur ein Entwurf, aber ich denke, Sie bekommen die Idee):

// 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 könnte zum Beispiel:/sdcard/Android /data/org.example.acceptance/files/coverage.ec

Nun werden die Jacoco-Daten auf das Smartphone geschrieben. Als nächstes müssen wir diese Datei herunterladen. Sie können

verwenden
appiumDriver.pullFile(logFilePath); 

Nun müssen Sie die Datei „jacoco-it.exec“ kopieren (die immer angehängt werden soll, wenn Sie die Datei ziehen) in build/jacoco/jacoco-it.exec sehen gradle .build oben und laufen

gradlew sonarRunner 

In Sonarqube die Integration Test Coverage Widget hinzufügen und Sie sollten jetzt einige Werte ...

Leider Code-Coverage wird nicht funktionieren, wenn Sie mit retrolambda (wie wir sehen, machen).Retrolambda generiert anonyme Klassen, die nicht Teil der Quelldateien sind. Daher kann SonarQube sie nicht korrekt abgleichen und weist eine wesentlich geringere Codeabdeckung auf, als sie tatsächlich ist. Wenn jemand eine Lösung dafür findet, würde ich mich sehr freuen :-)

+0

bin ich etwas fehlt, oder verwenden Sie beide emma und jacoco hier? Ich habe ein ähnliches Problem, daher ist diese Antwort sehr interessant - aber ich kann es nicht verstehen. – Vish

0

Ich löste dieses Problem, indem ich Broadcast-Empfänger zu der Anwendung, die Sie testen, hinzufügte! (Können Sie den Empfänger nur zu Debug-Ordner hinzufügen, keine Notwendigkeit, führen für sie in Hauptquelle existieren)

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 add (können Sie diesen Debug-Ordner hinzufügen, damit es nicht in Hauptquelle existieren)

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

im build.gradle der Anwendung hinzugefügt I

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 

     } 
    } 

Sie bauen Ihre Anwendung als Debug, dann installieren und ausführen. adb Pull /mnt/sdcard/coverage.exec

, nachdem Sie laufen diese -

send "broadcast -a com.example.action Shell am adb" coverage.exec Pull Abdeckung von Gerät erstellen ADB ausgestrahlt durch Sie müssen die Abdeckung aus der Datei

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

diese Aufgabe ist eine Möglichkeit schaffen Berichterstattung Dateien in coverageSourceDirs fügen Sie alle Standorte Ihres applicaiton Quellcode zu erstellen, so dass er, welcher Code zu nehmen und schaffen Berichterstattung basiert wissen auf ihnen executionData ist der Ort wo Sie die coverage.exec, die Sie aus dem Gerät

gezogen haben Führen Sie die Aufgabe die Dateien werden für HTML und XML erstellt können Sie auch csv hinzufügen (beachten Sie, es wird im Build-Ordner der Anwendung erstellen)!

kennen müssen, müssen Sie die Aufgabe gegen den gleichen Code ausführen Sie Ihre Anwendung Debug-Version gebaut