如何测试Gradle插件


在本文中,我分享了为自定义Gradle插件创建功能测试以及如何配置插件项目以从测试中收集代码覆盖率指标的经验。

在上一篇文章中,我描述了如何构建自定义Gradle插件。在这里,我们将继续与之合作。在开始之前,建议您重新编写上一篇文章,以更好地了解我们的起点。

0.克隆项目

git clone -b chapter-1 https://github.com/SurpSG/code-lines-counter-gradle-plugin.git

1.配置 创建一个单独的源集,其中将包含功能测试。首先创建一个简单目录src/funcTests/kotlin

1586465385282.png 项目文件结构创建后,该目录看起来像一个普通文件夹,并且未被IDE识别为代码源。

现在该向Gradle解释该目录将包含代码,或者换句话说,使其成为“ sourceSet”。

打开项目根目录中的“ build.gradle ”文件,添加新的源集配置,然后重新导入项目:

sourceSets {
    functionalTest { // declares a new sourceset
        // specifies code source dir
        kotlin.srcDir file('src/funcTests/kotlin') 
        // specifies resource dir
        resources.srcDir file('src/funcTests/resources') 

        // specifies dependencies to compile test classes
        compileClasspath += sourceSets.main.output + configurations.testRuntimeClasspath

        // specifies dependencies to run tests
        runtimeClasspath += output + compileClasspath
    }
}

创建一个将运行功能测试的Gradle任务,并为测试配置添加Kotlin std lib依赖项:

task functionalTest(type: Test) {
    description = 'Runs the functional tests.'
    group = 'verification'
    testClassesDirs = sourceSets.functionalTest.output.classesDirs
    classpath = sourceSets.functionalTest.runtimeClasspath
}
check.dependsOn functionalTest

2.测试创建 您可以使用任何您喜欢的测试框架。在此示例中,我使用JUnit 4.12。创建com.github.CodeLinesPluginTest Kotlin类:

class CodeLinesPluginTest {

    // creates temp directory for a gradle project                      <-------- (1)
    @get:Rule
    var testProjectDir = TemporaryFolder()

    private lateinit var buildFile: File
    private lateinit var gradleRunner: GradleRunner

    @Before
    fun setup() {
        // creates empty build.gradle file in the test gradle project   <-------- (2)
        buildFile = testProjectDir.newFile("build.gradle")

        // creates and configures gradle runner                         <-------- (3)
        gradleRunner = GradleRunner.create()
            .withPluginClasspath()
            .withProjectDir(testProjectDir.root)
            .withTestKitDir(testProjectDir.newFolder())
    }

    @Test
    fun `check test setup`() {
        // runs `tasks` gradle task                                     <-------- (4)
        val result = gradleRunner
            .withArguments("tasks")

            .build()

        println(result.output)
    }
}

在上面的示例中,这是一个简单的功能测试。该测试创建了一个摇篮工程,并运行任务的摇篮任务。让我们一步一步地探索测试:

  1. 声明关心临时目录创建的规则。该目录用作项目根目录。
  2. 在项目的根目录中创建一个空的build.gradle 文件。
  3. 创建一个Gradle Runner,它将帮助我们设置/构建/运行测试Gradle项目。
  4. 执行Gradle任务,即task。Gradle Runner返回用于断言的执行结果 。 运行测试,观察基本的Gradle任务已打印到控制台。我们刚刚检查了我们的配置是否正确。

应用“代码行”插件:

@Before
fun setup() {
    buildFile = testProjectDir.newFile("build.gradle")

    // add common configuration for all tests in this class
    buildFile.appendText("""
        plugins {
            id 'java'                      // `code-lines` plugin is dependent on `java` plugin
            id 'com.github.code-lines'
        }

    """.trimIndent())

    ...
}

然后更新测试:

@Test
fun `codeLines task should print '0' when there is no source code`() {
    val result = gradleRunner
        .withArguments("codeLines")                             // <------- (1)
        .build()

    assertEquals(SUCCESS, result.task(":codeLines")!!.outcome)  // <------- (2)
    assertTrue(result.output.contains("Total lines: 0"))        // <------- (3)
}

现在,测试执行以下步骤:

  1. 调用codeLines 任务。
  2. 验证codeLines的执行状态。
  3. 验证输出是否包含预期的消息。结果是“总行数:0”,因为测试项目没有任何代码。 这是该插件最简单的快乐路径测试。我们再添加一个:

添加一个Java类以验证插件正确计数行数。 应用非默认插件的配置。 按位置创建一个简单的Java类 code-lines-counter-gradle-plugin/src/funcTests/resources/TestClass.java

public class TestClass {

    public static void main(String[] args) {
        System.out.println("Hello world");
    }
}

添加一个新测试:

@Test
fun `codeLines task should print 'Total lines 6'`() {
    // creates folders in the temp project
    val testClassLocation: File = testProjectDir.newFolder("src", "main", "java").resolve("TestClass.java")

    CodeLinesPluginTest::class.java.classLoader
        .getResource("TestClass.java")!!.file.let(::File)
        .copyTo(testClassLocation)  // copies test file from resources to test project 

    val result = gradleRunner
        .withArguments("codeLines")
        .build()

    assertEquals(SUCCESS, result.task(":codeLines")!!.outcome)
    assertTrue(result.output.contains("Total lines: 6"))
}

再添加一个测试以检查插件的配置:

@Test
fun `codeLines should skip blank lines`() {
    val testClassLocation: File = testProjectDir.newFolder("src", "main", "java").resolve("TestClass.java")
    CodeLinesPluginTest::class.java.classLoader
        .getResource("TestClass.java")!!.file.let(::File)
        .copyTo(testClassLocation)

    // apply `code-lines` plugin configuration
    buildFile.appendText("""
        codeLinesStat {
            sourceFilters.skipBlankLines = true 
        }
    """.trimIndent())

    val result = gradleRunner
        .withArguments("codeLines")
        .build()

    assertEquals(SUCCESS, result.task(":codeLines")!!.outcome)
    assertTrue(result.output.contains("Total lines: 5"))
}

3.代码覆盖率 应用Jacoco插件。打开build.gradle并使用以下命令进行更新:

plugins {  
    id 'jacoco'
}

jacocoTestReport {
    reports.html.enabled = true
    executionData.setFrom fileTree(buildDir).include("/jacoco/*.exec")  // <------ (1) 
}

jacoco {
    toolVersion = '0.8.5'     // <----- (2)
}

指定要分析的覆盖率数据文件。因为我们有两个单独的测试源集,所以我们需要告诉Jacoco。设置Jacoco版本。我更喜欢最新版本,尤其是当我使用Kotlin时。

在这一步,我们应该添加一些额外的配置。使用TestKit执行的测试在守护程序JVM中运行。这就是为什么我们需要告诉守护程序JVM使用Jacoco Java代理。

幸运的是,我们可以使用Jacoco-gradle-testkit-plugin在这里为我们提供帮助:

plugins {
    id "pl.droidsonroids.jacoco.testkit" version "1.0.5"
}

functionalTest.dependsOn generateJacocoTestKitProperties
generateJacocoTestKitProperties.destinationFile = file("$buildDir.absolutePath/jacoco/functional.exec")

CodeLinesPluginTest 类中更新GradleRunner配置:

gradleRunner = GradleRunner.create()
            .withPluginClasspath()
            .withProjectDir(testProjectDir.root)
            .withTestKitDir(testProjectDir.newFolder())
            .apply {
                // gradle testkit jacoco support
                File("./build/testkit/testkit-gradle.properties")
                    .copyTo(File(projectDir, "gradle.properties"))
            }

现在,我们可以运行测试并检查覆盖率:

gradlew check jacocoTestReport

在您喜欢的浏览器中打开{PROJECT-ROOT}/build/reports/jacoco/test/html/index.html

1587843909231.png

我建议您在项目中再使用一个插件。DiffCoverage 基于新的或修改的代码构建覆盖报告。这可能有助于保持较高的代码覆盖率。

完整的工作示例跟随链接或克隆项目:

git clone -b chapter-2-testing https://github.com/SurpSG/code-lines-counter-gradle-plugin.git

结论 功能测试很重要,因为它们会告诉您插件是否正常工作。您可以检查插件,但不要过度创建测试,因为它们在性能方面非常差。首选常见的快乐路径方案,例如检查插件的配置,插件的任务生命周期,与其他插件的交互。

代码覆盖率工具有助于检测未经测试的代码,因此潜在地,您可以在提交代码之前发现并修复缺陷。不要完全依赖此类工具,因为它们不能保证涵盖所有极端情况。它们只是告诉您是否在测试运行时调用了代码。


原文链接:http://codingdict.com/