测试的并行执行

26

我注意到SBT正在并行运行我的specs2测试。这似乎是好的,但其中一个测试涉及从文件中读取和写入数据,因此会产生不可预测的失败,如下所示。

除了以下两种方法之外,还有更好的选择吗?

  1. 将所有测试设置为串行运行,
  2. 为每个测试使用单独的文件名和tear-downs?
class WriteAndReadSpec extends Specification{
  val file = new File("testFiles/tmp.txt")

  "WriteAndRead" should {
    "work once" in {
      new FileWriter(file, false).append("Foo").close
      Source.fromFile(file).getLines().toList(0) must_== "Foo"
    }
    "work twice" in {
      new FileWriter(file, false).append("Bar").close
      Source.fromFile(file).getLines().toList(0) must_== "Bar"
    }
  }

  trait TearDown extends After {
    def after = if(file.exists) file.delete
  }
}
5个回答

36

除了上面提到的有关sbt的内容外,您还必须知道specs2默认情况下会同时运行规范的所有示例。

您仍然可以声明对于给定的规范,示例必须按顺序执行。要做到这一点,您只需将 sequential 添加到规范的开头即可:

class WriteAndReadSpec extends Specification{
  val file = new File("testFiles/tmp.txt")

  sequential

  "WriteAndRead" should {
   ...
  }
}

1
这是拼图中缺失的一块。事实上,它甚至可以在没有其他答案建议的自定义Build.scala的情况下工作。我猜SBT并行性是跨越不同的测试文件而不是单个文件内部。 - Pengin
是的,sbt在测试之间控制这一点,顺序执行是在测试用例中使用的正确方法。将其保持靠近测试规范也更有意义,因为这样它就被隐式记录下来了。 - Elmar Weber

4

固定测试套件的顺序可能导致测试用例之间相互依赖,增加维护成本。

我更倾向于在不触及文件系统的情况下进行测试(无论是业务逻辑还是序列化代码),或者如果必须要涉及文件系统(例如测试与文件传输的集成),则会使用创建临时文件的方式:

// Create temp file.
File temp = File.createTempFile("pattern", ".suffix");
// Delete temp file when program exits.
temp.deleteOnExit();

3

他的回答中,Pablo Fernandez提供的wiki链接相当不错,虽然其中的示例存在一个小错误,可能会让人感到困惑(但作为一个wiki,我已经进行了更正)。这里有一个project/Build.scala文件,它实际上可以编译并产生预期的过滤器,但我没有真正尝试它是否适用于测试。

import sbt._
import Keys._

object B extends Build
{
  lazy val root =
    Project("root", file("."))
      .configs( Serial )
      .settings( inConfig(Serial)(Defaults.testTasks) : _*)
      .settings(
         libraryDependencies ++= specs,
         testOptions in Test := Seq(Tests.Filter(parFilter)),
         testOptions in Serial := Seq(Tests.Filter(serialFilter))
       )
      .settings( parallelExecution in Serial := false : _*)

  def parFilter(name: String): Boolean = !(name startsWith "WriteAndReadSpec")
  def serialFilter(name: String): Boolean = (name startsWith "WriteAndReadSpec")

  lazy val Serial = config("serial") extend(Test)

  lazy val specs = Seq(
        "org.specs2" %% "specs2" % "1.6.1",
        "org.specs2" %% "specs2-scalaz-core" % "6.0.1" % "test"
      )
}

感谢您指出这是一个项目/Build.scala文件,我一直在苦苦挣扎。在运行serial:test时,不得不将过滤器更改为使用“contains”而不是“startsWith”,然后才看到我的测试。不幸的是,我仍然遇到了不可预测的行为,所以猜测它仍在并行运行。 - Pengin

2
似乎有第三种选择,即将串行测试分组配置并单独运行它们,同时并行运行其余部分。
请查看此wiki,查找"应用于并行执行"

0

其他答案已经解释了如何使它们顺序运行。

虽然它们是有效的答案,但我认为最好更改您的测试以让它们并行运行。(如果可能的话)

在您的示例中-为每个测试使用不同的文件。 如果涉及到数据库-请使用不同的(或随机的)用户(或任何您可以隔离的内容) 等等...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接