GradleRunner testing tasks that create files

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"
    }
}