Test file structure in groovy(Spock)

994 Views Asked by At

How to test created and expected file tree in groovy(Spock)? Right now I'm using Set where I specify paths which I expect to get and collecting actual paths in this way:

Set<String> getCreatedFilePaths(String root) {
    Set<String> createFilePaths = new HashSet<>()
    new File(root).eachFileRecurse {
        createFilePaths << it.absolutePath
    }
    return createFilePaths
}

But the readability of the test isn't so good. Is it possible in groovy to write expected paths as a tree, and after that compare with actual

For example, expected:

region:
     usa:
       new_york.json
       california.json
  europe:
       spain.json
       italy.json

And actual will be converted to this kind of tree.

3

There are 3 best solutions below

3
On BEST ANSWER

Not sure if you can do it with the built-in recursive methods. There certainly are powerful ones, but this is standard recursion code you can use:

def path = new File("/Users/me/Downloads")

def printTree(File file, Integer level) {
    println "   " * level + "${file.name}:"
    file.eachFile {
        println "   " * (level + 1) + it.name
    }
    file.eachDir {
        printTree(it, level + 1)
    }
}

printTree(path, 1)

That prints the format you describe

0
On

Modified Bavo Bruylandt answer to collect file tree paths, and sort it to not care about the order of files.

def "check directory structure"() {
    expect:
    String created = getCreatedFilePaths(new File("/tmp/region"))
    String expected = new File("expected.txt").text

    created == expected
}

private String getCreatedFilePaths(File root) {
        List paths = new ArrayList()
        printTree(root, 0, paths)
        return paths.join("\n")
    }

private void printTree(File file, Integer level, List paths) {
        paths << ("\t" * level + "${file.name}:")
        file.listFiles().sort{it.name}.each {
            if (it.isFile()) {
                paths << ("\t" * (level + 1) + it.name)
            }
            if (it.isDirectory()) {
                collectFileTree(it, level + 1, paths)
            }
        }
    }

And expected files put in the expected.txt file with indent(\t) in this way:

region:
    usa:
        new_york.json
        california.json
    europe:
        spain.json
        italy.json
2
On

You can either build your own parser or use Groovy's built-in JSON parser:

package de.scrum_master.stackoverflow

import groovy.json.JsonParserType
import groovy.json.JsonSlurper
import spock.lang.Specification

class FileRecursionTest extends Specification {
  def jsonDirectoryTree = """{
      com : {
        na : {
          tests : [
            MyBaseIT.groovy
          ]
        },
        twg : {
          sample : {
            model : [
              PrimeNumberCalculatorSpec.groovy
            ]
          }
        }
      },
      de : {
        scrum_master : {
          stackoverflow : [
            AllowedPasswordsTest.groovy,
            CarTest.groovy,
            FileRecursionTest.groovy,
            {
              foo : [
                LoginIT.groovy,
                LoginModule.groovy,
                LoginPage.groovy,
                LoginValidationPage.groovy,
                User.groovy
              ]
            },
            LuceneTest.groovy
          ],
          testing : [
            GebTestHelper.groovy,
            RestartBrowserIT.groovy,
            SampleGebIT.groovy
          ]
        }
      }
    }"""

  def "Parse directory tree JSON representation"() {
    given:
    def jsonSlurper = new JsonSlurper(type: JsonParserType.LAX)
    def rootDirectory = jsonSlurper.parseText(jsonDirectoryTree)

    expect:
    rootDirectory.de.scrum_master.stackoverflow.contains("CarTest.groovy")
    rootDirectory.com.twg.sample.model.contains("PrimeNumberCalculatorSpec.groovy")

    when:
    def fileList = objectGraphToFileList("src/test/groovy", rootDirectory)
    fileList.each { println it }

    then:
    fileList.size() == 14
    fileList.contains("src/test/groovy/de/scrum_master/stackoverflow/CarTest.groovy")
    fileList.contains("src/test/groovy/com/twg/sample/model/PrimeNumberCalculatorSpec.groovy")
  }

  List<File> objectGraphToFileList(String directoryPath, Object directoryContent) {
    List<File> files = []
    directoryContent.each {
      switch (it) {
        case String:
          files << directoryPath + "/" + it
          break
        case Map:
          files += objectGraphToFileList(directoryPath, it)
          break
        case Map.Entry:
          files += objectGraphToFileList(directoryPath + "/" + (it as Map.Entry).key, (it as Map.Entry).value)
          break
        default:
          throw new IllegalArgumentException("unexpected directory content value $it")
      }
    }
    files
  }

}

Please note:

  • I used new JsonSlurper(type: JsonParserType.LAX) in order to avoid having to quote each single String in the JSON structure. If your file names contain spaces or other special characters, you will have to use something like "my file name", though.

  • In rootDirectory.de.scrum_master.stackoverflow.contains("CarTest.groovy") you can see how you can nicely interact with the parsed JSON object graph in .property syntax. You might like it or not, need it or not.

  • Recursive method objectGraphToFileList converts the parsed object graph to a list of files (if you prefer a set, change it, but File.eachFileRecurse(..) should not yield any duplicates, so the set is not needed.

  • If you do not like the parentheses etc. in the JSON, you can still build your own parser.

  • You might want to add another utility method to create a JSON string like the given one from a validated directory structure, so you have less work when writing similar tests.