用法
想使用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
}