2015-06-22 91 views
6

我們正在構建一個使用Appium進行測試的Android應用程序。現在我想看看我們的Appium測試的測試覆蓋率。 我認爲這是可能的,因爲Jacoco支持離線檢測(http://www.eclemma.org/jacoco/trunk/doc/offline.html)。Android Gradle Jacoco:用於集成測試的脫機儀器

而且jacoco gradle這個插件甚至文件說:

雖然類型測試的所有任務都自動增強時,已經應用了Java插件來提供覆蓋信息,實現JavaForkOptions任何任務都可以得到增強由JaCoCo插件。也就是說,任何支持Java進程的任務都可以用來生成覆蓋信息。

看到https://docs.gradle.org/current/userguide/jacoco_plugin.html

但我怎麼都寫的build.gradle所以我們的驗收調試味道儀表當Appium測試執行,甚至手工測試用例的EXEC文件寫入到智能手機被執行? 因爲那時我可以提取exec文件並將其發送到SonarQube以供進一步分析。

感謝 本

回答

1

最後我設法它得到它的工作,我想與大家分享的解決方案:

使儀器爲您buildType並配置相應SonarQube例如

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

以下內容添加到您的AndroidManifest.xml

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

CoverageDataDumper應該看起來像:

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

然後運行Appium測試用例驗收味道應用(與儀表類)。致電前「重置應用」或「關閉應用程序」請確保調用以下方法(只是一個草案,但我認爲你的想法):

// 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可能是例如:/ SD卡/安卓/data/org.example.acceptance/files/coverage.ec

現在Jacoco數據被寫入智能手機。接下來我們需要下載該文件。您可以使用

appiumDriver.pullFile(logFilePath); 

現在你需要複製該文件「jacoco-it.exec」(這應該永遠是附加當你拉文件)到編譯/ jacoco/jacoco-it.exec的gradle見.build以上並運行

gradlew sonarRunner 

在SonarQube添加集成測試覆蓋率的Widget,你現在一些值應該看到...

不幸的是,如果你使用的是retrolambda代碼覆蓋率將無法正常工作(因爲我們做)。Retrolambda將生成不屬於源文件的匿名類 - 所以SonarQube無法正確匹配它們,並且顯示的代碼覆蓋率比實際低得多。如果有人找到了解決方案,我會很高興:-)

+0

我缺少的東西,或者是你同時使用Emma和jacoco這裏?我有類似的問題,所以這個答案非常有趣 - 但我無法理解。 – Vish

0

我解決了這個問題,通過添加廣播接收器到您測試的應用程序! (你可以將接收器僅添加到debug文件夾會導致不需要它的主要來源存在)

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

在manefist插件(你可以,這樣就不會在主要來源存在添加這個debug文件夾)

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

在應用的build.gradle我加

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 

     } 
    } 

你建立你的應用程序作爲調試,而不是安裝和運行它。

發送亞行「亞洲開發銀行殼AM廣播-a com.example.action」播出從設備創建coverage.exec 拉覆蓋 - 亞洲開發銀行拉/mnt/sdcard/coverage.exec

運行此之後你需要從文件

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

這個任務是建立覆蓋文件 在coverageSourceDirs添加您的應用程序了源代碼,所有的位置的一種方式,所以它會知道採取哪些代碼和基於覆蓋創建創建覆蓋 executionData是位置你把你從設備上取下的coverage.exec

運行任務 這些文件將爲html和xml創建,您還可以添加csv(注意它將在應用程序的build文件夾中創建)!

要知道,你必須運行鍼對相同的代碼的任務,您構建了應用程序的調試版本