如何在 Scala 单元测试中创建临时目录

问题描述 投票:0回答:3

在 Scala 中,单元测试如何创建临时目录以用作测试的一部分?

我正在尝试对依赖于目录的类进行单元测试

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

我正在寻找以下形式的东西:

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

  val usesDirectory = UsesDirectory(tempDir)

  "UsesDirectory" should {
    ...
  }
}

此外,任何关于在单元测试完成后适当清理资源的意见/建议都会有所帮助。

提前感谢您的考虑和回复。

scala unit-testing filesystems scalatest
3个回答
11
投票

Krzysztof 的回答提供了一个很好的策略,可以避免在测试中完全需要临时目录。

但是,如果您确实需要

UsesDirectory
来处理真实文件,您可以执行以下操作来创建一个临时目录:

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

关于清理,您可以使用 JVM 关闭挂钩机制来删除您的临时文件。

java.io.File
确实提供了
deleteOnExit()
方法,但它不适用于非空目录)

您可以使用

sys.addShutdownHook {}
实现自定义关闭挂钩,并使用
Files.walk
Files.walkTree
删除临时目录的内容。

您可能还想看看更好的文件库,它为常见的文件操作提供了一个不太冗长的 scala API,包括

File.newTemporaryDirectory()
file.walk()


3
投票

File
在Java中测试起来非常麻烦。没有简单的方法来创建某种虚拟文件系统抽象,可用于测试。

一个很酷的解决方法是创建某种包装器,可用于存根和模拟。

例如:

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

然后你必须在课堂上使用它而不是

File

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) 

那有什么好处?

在您的测试中,您可以提供

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 {
      ...
    }
}

如果您使用这种或类似的方法,您甚至不需要在单元测试中接触文件系统。


0
投票

我遇到了类似的要求,要针对示例文件对我的功能进行单元测试。我使用

scalatest.BeforeAndAfterAll
包中的
scalatest
特性解决了它。

import com.nag.SensorApp
import com.nag.repository.SensorStats
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.{BeforeAndAfterAll}

import java.io.File
import java.io.FileWriter
import java.nio.file.{Files, Path, Paths}
import scala.collection.mutable


class AggregatorServiceTests extends AnyFlatSpec with BeforeAndAfterAll {

  val testFileName = "test-files"
  val configMap = mutable.Map.empty[String, File]

接下来我们覆盖

beforeAll()
方法,它可以在测试前设置tempFile。相应的
afterAll()
将确保在执行测试后清理这些临时文件。


  override def beforeAll() = {
    val tempDir = Files.createTempDirectory(testFileName)
    print("The temporary directory = " +(tempDir))
    val tempFile = new File(osAwareFilename(tempDir, testFileName))
    tempFile.createNewFile()
    configMap.put(testFileName, tempFile)
    val myWriter = new FileWriter(tempFile)
    myWriter.write("sensor-id,humidity\ns1,10\ns2,88\ns1,NaN")
    myWriter.close()
  }

  override def afterAll(): Unit = {
    super.afterAll()
    val tempFile = configMap.get(testFileName).get
    tempFile.delete()
  }

  private def osAwareFilename(inputPath : Path, filename : String) : String = {
    if (System.getProperty("os.name").contains("Windows")) {
      inputPath.toFile.getPath + "\\" + filename
    }
    else {
      inputPath.toFile.getPath + "/" + filename
    }
  }

完成此设置后,可以从

tempFile
访问
configMap
并在我们的测试中使用。

  "The Aggregator Service" should "sum sensor s1 and s2 records ignoring NaN " in  {
    val fileForTest = configMap.get(testFileName).get
    println("File for test :: "+ fileForTest.toPath.getParent)
    val aggregatorService = SensorApp.main(fileForTest.toPath.getParent)
    assert(SensorStats.totalRowsProcessed == 3)
    assert(SensorStats.totalFailedMeasurements == 1)
    assert(SensorStats.invalidSensors.keys.size == 0)
    assert(SensorStats.sensorInfo.get("s1").get.sum == 10)

  }

}
© www.soinside.com 2019 - 2024. All rights reserved.