Spocklight: Creating Temporary Files And Directories With FileSystemFixture
If we write specification where we need to use files and directories we can use the @TempDir
annotation on a File
or Path
instance variable. By using this annotation we make sure the file is created in the directory defined by the Java system property java.io.tmpdir
. We could overwrite the temporary root directory using Spock configuration if we want, but the default should be okay for most situations. The @TempDir
annotation can actually be used on any class that has a constructor with a File
or Path
argument. Since Spock 2.2 we can use the FileSystemFixture
class provided by Spock. With this class we have a nice DSL to create directory structures and files in a simple matter. We can use the Groovy extensions to File
and Path
to also immediately create contents for the files. If we want to use the extensions to Path
we must make sure we include org.apache.groovy:groovy-nio
as dependency to our test runtime classpath. The FileSystemFixture
class also has the method copyFromClasspath
that we can use to copy files and their content directory into our newly created directory structure.
In the following example specification we use FileSystemFixture
to define a new directory structure in a temporary directory, but also in our project directory:
package mrhaki
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.TempDir
import spock.util.io.FileSystemFixture
import java.nio.file.Path
import java.nio.file.Paths
class FileFixturesSpec extends Specification {
/**
* Class we want to test. The class has a method
* File renderDoc(File input, File outputDir) that takes
* an input file and stores a rendered file in the given
* output directory.
*/
@Subject
private DocumentBuilder documentBuilder = new DocumentBuilder()
/**
* With the TempDir annotation we make sure our directories and
* files created with FileSystemFixture are deleted after
* each feature method run.
*/
@TempDir
private FileSystemFixture fileSystemFixture
void "convert document"() {
given:
// Create a new directory structure in the temporary directory
// <root>
// +-- src
// | +-- docs
// | +-- input.adoc
// | +-- convert.adoc
// +-- output
fileSystemFixture.create {
dir("src") {
dir("docs") {
// file(String) returns a Path and with
// groovy-nio module on the classpath we can use
// extensions to add text to file. E.g. via the text property.
file("input.adoc").text = '''\
= Sample
Welcome to *AsciidoctorJ*.
'''.stripIndent()
// Copy file from classpath (src/test/resources)
// and rename it at the same time.
// Without rename it would be
// copyFromClasspath("/samples/sample.adoc")
copyFromClasspath("/samples/sample.adoc", "convert.adoc")
}
}
dir("output")
}
and:
// Using resolve we get the actual Path to the file
Path inputDoc = fileSystemFixture.resolve("src/docs/input.adoc")
Path convertDoc = fileSystemFixture.resolve("src/docs/convert.adoc")
// We can also use Paths to resolve the actual Path.
Path outputDir = fileSystemFixture.resolve(Paths.get("output"))
when:
File resultDoc = documentBuilder.renderDoc(inputDoc.toFile(), outputDir.toFile())
then:
resultDoc.text =~ "<p>Welcome to <strong>AsciidoctorJ</strong>.</p>"
when:
File resultConvert = documentBuilder.renderDoc(convertDoc.toFile(), outputDir.toFile())
then:
resultConvert.text =~ "<p>Testing <strong>AsciidoctorJ</strong> with Spock ๐</p>"
}
void "convert document from non-temporary dir"() {
given:
// Create FileSystemFixture in our project build directory.
FileSystemFixture fileSystemFixture = new FileSystemFixture(Paths.get("build"))
fileSystemFixture.create {
dir("test-docs") {
dir("src") {
dir("docs") {
copyFromClasspath("/samples/sample.adoc")
}
}
dir("output")
}
}
and:
Path convertDoc = fileSystemFixture.resolve("test-docs/src/docs/sample.adoc")
Path outputDir = fileSystemFixture.resolve(Paths.get("test-docs/output"))
when:
File resultDoc = documentBuilder.renderDoc(convertDoc.toFile(), outputDir.toFile())
then:
resultDoc.text =~ "<p>Testing <strong>AsciidoctorJ</strong> with Spock ๐</p>"
cleanup:
// We can delete the test-docs directory ourselves.
fileSystemFixture.resolve("test-docs").deleteDir()
}
}
In order to use the Groovy extensions for java.nio.Path
we must add the groovy-nio
module to the test classpath. For example we can do this if we use Gradle by using the JVM TestSuite plugin extension:
plugins {
java
groovy
}
...
repositories {
mavenCentral()
}
testing {
suites {
val test by getting(JvmTestSuite::class) {
// Define we want to use Spock.
useSpock("2.3-groovy-4.0")
dependencies {
// Add groovy-nio module.
implementation("org.apache.groovy:groovy-nio")
}
}
}
}
Written with Spock 2.3-groovy-4.0 and Gradle 8.0.2.