Gradle学习(二十一)——用TestKit测试构建逻辑

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lastsweetop/article/details/79509267

用法

想使用TestKit,需要在构建脚本中引入:

dependencies {
    testCompile gradleTestKit()
}

gradleTestKit()方法包含TestKit的class文件,还有Gradle Api的客户端工具类,但是并不包含JUnit,TestNG等测试工具的类,

用GradleRunner进行功能性测试

GradleRunner有助于以编程的方式执行Gradle构建,并且可以检测结果。

可以创建人为的构建()以编程的方式,或者模板的方式)来执行将要被测试的构建逻辑,构建可以以多种潜在的方式执行(各种任务和参数的组合),并且根据以下可能存在的组合来校验构建逻辑:

  • 构建的输出
  • 构建的日志(控制台日志)
  • 执行构建的任务和构建结果构成的集合

在创建和配置runner的实例后,构建可以通过GradleRunner.build()或者GradleRunner.buildAndFail()执行,并且返回预期的结果以供校验。

下面来看下通过Junit来使用GradleRunner

import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.GradleRunner;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;

public class BuildLogicFunctionalTest1 {
    @Rule
    public final TemporaryFolder testProjectDir = new TemporaryFolder();
    private File buildFile;

    @Before
    public void setup() throws IOException {
        buildFile = testProjectDir.newFile("build.gradle");
    }

    @Test
    public void testHelloWorldTask() throws IOException {
        String buildFileContent = "task helloWorld {" +
                "    doLast {" +
                "        println 'Hello world!'" +
                "    }" +
                "}";
        writeFile(buildFile, buildFileContent);

        BuildResult result = GradleRunner.create()
                .withProjectDir(testProjectDir.getRoot())
                .withArguments("helloWorld")
                .build();

        assertTrue(result.getOutput().contains("Hello world!"));
        assertEquals(TaskOutcome.SUCCESS, result.task(":helloWorld").getOutcome());
    }

    private void writeFile(File destination, String content) throws IOException {
        BufferedWriter output = null;
        try {
            output = new BufferedWriter(new FileWriter(destination));
            output.write(content);
        } finally {
            if (output != null) {
                output.close();
            }
        }
    }
}

可以使用任何测试执行的框架

由于Gradle的脚本是由Groovy编写的,而且很多Gradle的插件也是用Groovy写的,因此用Groovy来写Gradle的功能测试也是一种不错的选择,推荐使用Spock测试框架,相较于JUnit它有很多好的特性

下面是通过Groovy语言的Spock框架来使用GradleRunner的测试

import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.TaskOutcome
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Specification

class BuildLogicFunctionalTest extends Specification {
    @Rule
    public final TemporaryFolder testProjectDir = new TemporaryFolder()
    private File buildFile;

    def setup() {
        buildFile = testProjectDir.newFile('build.gradle')
    }
    def "hello world task prints hello world"() {
        given:
        buildFile << """
            task helloWorld {
                doLast {
                    println 'Hello world!'
                }
            }
        """
        when:
        def result = GradleRunner.create()
                .withProjectDir(testProjectDir.getRoot())
                .withArguments("helloWorld")
                .build()

        then:
        result.getOutput().contains("Hello world!")
        result.task(":helloWorld").getOutcome() == TaskOutcome.SUCCESS
    }


}

将需要测试的插件代码放到测试构建中

GradleRunner是使用Tooling API来执行构建的,这意味着测试构建是个单独的进程,和执行测试的进程是相互隔离的,执行构建并会不会和测试进程共享相同的classpath或者classloader,执行测试的代码也是对测试构建不可见的。

java-gradle-plugin插件可以有助于开发Gradle插件,从2.13版本开始直接集成了TestKit,当被引入到项目之后,它会自动增加gradleTestKit()testCompile配置中,此外他还自动生成需要测试的classpath,并且通过GradleRunner.withPluginClasspath()把classpath注入到用户创建的GradleRunner实例中,需要注意的一点是,目前只有在Plugins的DSL形式运行,apply plugin是不行的,这个坑了我好几个小时,不知道错在哪里。

我们来通过例子进一步看下;

这是需要测试的插件
src/main/groovy/com/lastsweetop/plugin/GreetingPlugin.groovy

package com.lastsweetop.plugin

import org.gradle.api.Plugin
import org.gradle.api.Project

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.task('hello') {
            doLast {
                println 'Hello from the GreetingPlugin'
            }
        }
    }
}

这是插件的描述文件
src/main/resources/META-INF/gradle-plugins/com.lastsweetop.plugin.greeting.properties

implementation-class=com.lastsweetop.plugin.GreetingPlugin

build.gradle加入java-gradle-plugin

apply plugin: 'java-gradle-plugin'

最后就是测试代码了:

import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.TaskOutcome
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Specification

class BuildLogicFunctionalTest extends Specification {
    @Rule
    public final TemporaryFolder testProjectDir = new TemporaryFolder()
    private File buildFile
    List<File> pluginClasspath

    def setup() {
        buildFile = testProjectDir.newFile('build.gradle')
    }

    def "hello world task prints hello world"() {
        given:
        buildFile << """
               plugins{
                id 'com.lastsweetop.plugin.greeting'
               }
        """
        when:
        def result = GradleRunner.create()
                .withProjectDir(testProjectDir.getRoot())
                .withArguments("hello")
                .withPluginClasspath()
                .build()

        then:
        result.getOutput().contains("Hello from the GreetingPlugin")
        result.task(":hello").getOutcome() == TaskOutcome.SUCCESS
    }


}

再次强调只能是

plugins{
 id 'com.lastsweetop.plugin.greeting'
}

不能apply

下面演示的是重新配置java-gradle-plugin的测试资源的集合。

sourceSets {
    functionalTest {
        groovy {
            srcDir file('src/functionalTest/groovy')
        }
        resources {
            srcDir file('src/functionalTest/resources')
        }
        compileClasspath += sourceSets.main.output + configurations.testRuntime
        runtimeClasspath += output + compileClasspath
    }
}

task functionalTest(type: Test) {
    testClassesDirs = sourceSets.functionalTest.output.classesDirs
    classpath = sourceSets.functionalTest.runtimeClasspath
}

check.dependsOn functionalTest

gradlePlugin {
    testSourceSets sourceSets.functionalTest
}

猜你喜欢

转载自blog.csdn.net/lastsweetop/article/details/79509267