I encountered some unexpected behaviour with the GradleRunner when testing a task that creates a file in the project root directory.
I was expecting the file to be created in the temporary project root directory that I passed to the GradleRunner but it is actually created in my code root directory.
My integration test is pretty straight forward:
public class integrationTests {
@Rule public final TemporaryFolder testProjectDir = new TemporaryFolder()
@Before
public void setup() throws IOException {
buildFile = testProjectDir.newFile('build.gradle')
buildFile << """
plugins {
id 'my.custom.plugin'
}
"""
propertiesFile = testProjectDir.newFile('gradle.properties')
propertiesFile << """
some_var=some_value
"""
}
@Test
public void doTest() {
BuildResult result = GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments("SomeTaskThatCreatesAFileInTheProjectRoot") //this task creates a file in my code root
.withPluginClasspath()
.build()
// what files do we have in the testProjectDir root? Only build.gradle and gradle.properties created above
testProjectDir.getRoot().eachFile() { file->
println file.getAbsolutePath()
}
result.task(":SomeTaskThatCreatesAFileInTheProjectRoot").outcome == "SUCCESS"
}
}
Is this the expected behavior of the GradleRunner or is it just how groovy behaves that I need to handle myself? As the file in question is generated from running a test, I’d also like it to be deleted after the test. Ideally I could put it in the testProjectDir that holds my test build.gradle and gradle.properties which is disposed of at the end of my test class by junit, but then I need to pass that temp directory location to SomeTaskThatCreatesAFileInTheProjectRoot as an -P argument basically?
It took a fair amount of messing around to get it right but things became a lot easier after learning about the spock test framework.
In the end the code looks something like this:
import org.junit.rules.TemporaryFolder
import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.BuildResult
import static org.gradle.testkit.runner.TaskOutcome.*
import spock.lang.Specification
import spock.lang.Shared
class integrationTests extends Specification{
@ClassRule
@Shared TemporaryFolder testProjectDir = new TemporaryFolder()
@Shared File buildFile
@Shared File propertiesFile
def setupSpec() {
buildFile = testProjectDir.newFile('build.gradle')
// setup temporary build file to apply our plugin
buildFile << """
plugins {
id 'gradle.plugin.id'
}
""".stripIndent()
// testkit-gradle.properties is a temporary file generated during the build before the tests run
// that contains information that jacoco needs to produce code coverage correctly
// see the createTestKitFiles task below
propertiesFile = writeFile('gradle.properties', getResourceUrl('testkit-gradle.properties').text)
propertiesFile << """
some_variable="some_value"
""".stripIndent()
}
def "some integration test"() {
// Build up our argument list for the gradle runner
given:
List<String> argList = new ArrayList<String>();
argList.add("task_to_run")
argList.add("-Pproject_directory=" + testProjectDir.root)
when:
def result = GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments(argList)
.withPluginClasspath()
.build()
then:
result.output.contains("some interesting string")
result.task(":task_to_run").outcome == SUCCESS
For completeness sake, here is the createTestkitFiles task:
task createTestkitFiles {
def outputDir = file("$buildDir/testkitFiles")
inputs.files configurations.jacocoRuntime
outputs.dir outputDir
// to combine the jacoco test coverage reports we need to pass some arguments to the jvm
// see here for more details https://discuss.gradle.org/t/testkit-jacoco-coverage/18792
doLast {
outputDir.mkdirs()
// annoying stuff if you have to build on windows.
String jacocoPath = configurations.jacocoRuntime.asPath.replace('\\', '/')
file("$outputDir/testkit-gradle.properties").text = "org.gradle.jvmargs=-javaagent:${jacocoPath}=destfile=$buildDir/jacoco/integTest.exec"
}
}