Android Gradle Jacoco:用于集成测试的离线工具

Ben*_*Ben 8 android offline gradle jacoco test-coverage

我们正在构建一个使用Appium进行测试的Android应用.现在我想看看Appium测试的测试覆盖率.我认为这是可能的,因为Jacoco支持离线仪器(http://www.eclemma.org/jacoco/trunk/doc/offline.html).

甚至jacoco gradle插件的文档也说:

虽然所有类型为Test的任务都会自动增强,以便在应用java插件时提供覆盖信息,但JaCoCo插件可以增强任何实现JavaForkOptions的任务.也就是说,任何分叉Java进程的任务都可用于生成覆盖信息.

请参阅https://docs.gradle.org/current/userguide/jacoco_plugin.html

但是我如何编写build.gradle,以便检测我们的验收调试风格,并在执行Appium测试或执行手动测试用例时将exec文件写入Smartphone?因为那时我可以提取exec文件并将其发送给SonarQube进行进一步分析.

谢谢Ben

Ben*_*Ben 2

最后我设法使其正常工作,我想与您分享解决方案:

为您的 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
    }
}
Run Code Online (Sandbox Code Playgroud)

将以下内容添加到您的 AndroidManifest.xml

<receiver
 android:name=".util.CoverageDataDumper"
 tools:ignore="ExportedReceiver">
 <intent-filter>
    <action android:name="org.example.DUMP_COVERAGE_DATA"/>
 </intent-filter>
</receiver>
Run Code Online (Sandbox Code Playgroud)

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 );
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

然后使用验收风味应用程序(带有检测类)运行您的 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." );
  }
}
Run Code Online (Sandbox Code Playgroud)

输出路径可以是例如:/sdcard/Android/data/org.example.acceptance/files/coverage.ec

现在 Jacoco 数据已写入智能手机。接下来我们需要下载该文件。您可以使用

appiumDriver.pullFile( logFilePath );
Run Code Online (Sandbox Code Playgroud)

现在您需要将文件“jacoco-it.exec”(在拉取文件时应始终附加)复制到 build/jacoco/jacoco-it.exec 请参阅上面的 gradle.build 并运行

gradlew sonarRunner
Run Code Online (Sandbox Code Playgroud)

在 SonarQube 中添加集成测试覆盖率小部件,您现在应该看到一些值...

不幸的是,如果您使用retrolambda(就像我们一样),代码覆盖率将不起作用。Retrolambda 将生成不属于源文件一部分的匿名类 - 因此 SonarQube 无法正确匹配它们,并且显示的代码覆盖率比实际低得多。如果有人找到解决方案,我会很高兴:-)