Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Create Temporary Directory in Scala Unit Tests

In scala how can a unit test create a temporary directory to use as part of the testing?

I am trying to unit test a class which depends on a directory

class UsesDirectory(directory : java.io.File) {
  ...
}

I'm looking for something of the form:

class UsesDirectorySpec extends FlatSpec {
  val tempDir = ??? //missing piece

  val usesDirectory = UsesDirectory(tempDir)

  "UsesDirectory" should {
    ...
  }
}

Also, any comments/suggestions on appropriately cleaning up the resource after the unit testing is completed would be helpful.

Thank you in advance for your consideration and response.

like image 764
Ramón J Romero y Vigil Avatar asked Mar 01 '19 14:03

Ramón J Romero y Vigil


2 Answers

Krzysztof's answer provides a good strategy for avoiding the need for temp directories in your tests altogether.

However if you do need UsesDirectory to work with real files, you can do something like the following to create a temporary directory:

import java.nio.file.Files
val tempDir = Files.createTempDirectory("some-prefix").toFile

Regarding cleanup, you could use the JVM shutdown hook mechanism to delete your temp files.

(java.io.File does provide deleteOnExit() method but it doesn't work on non-empty directories)

You could implement a custom shutdown hook using sys.addShutdownHook {}, and use Files.walk or Files.walkTree to delete the contents of your temp directory.

Also you may want to take a look at the better-files library, which provides a less verbose scala API for common files operations including File.newTemporaryDirectory() and file.walk()

like image 70
Dmitry Karlinsky Avatar answered Sep 28 '22 18:09

Dmitry Karlinsky


File in Java is very cumbersome to test. There is no simple way to create some kind of virtual filesystem abstraction, which can be used for tests.

A cool way around it is to create some kind of wrapper, that can be used for stubbing and mocking.

For example:

trait FileOps { //trait which works as proxy for file
  def getName(): String
  def exists(): Boolean
}

object FileOps {

  class FileOpsImpl(file: File) extends FileOps {
    override def getName(): String = file.getName //delegate all methods you need
    override def exists(): Boolean = file.exists()
  }

  implicit class FromFile(file: File) { //implicit method to convert File to FileOps
    def toFileOps: FileOpsImpl = new FileOpsImpl(file)
  }
}

Then you'd have to use it instead of File in your class:

class UsesDirectory(directory : FileOps) {
  ...
}

//maybe you can even create implicit conversion, but it's better to do it explicitly
val directory = new UserDirectory(file.toFileOps) 

And what is benefit of that?

In your tests you can provide custom implementation of FileOps:

class UsesDirectorySpec extends FlatSpec {
    val dummyFileOps = new FileOps {
        override def getName(): String = "mock"
        override def exists(): Boolean = true
    }

    //OR

    val mockFileOps = mock[FileOps] //you can mock it easily since it's only trait

    val usesDirectory = UsesDirectory(dummyFileOps)

    "UsesDirectory" should {
      ...
    }
}

If you use this or a similar approach, you don't even need to touch filesystem in your unit test.

like image 40
Krzysztof Atłasik Avatar answered Sep 28 '22 18:09

Krzysztof Atłasik