并行执行测试

发布于 2024-12-13 16:48:51 字数 718 浏览 0 评论 0 原文

我注意到 SBT 正在并行运行我的 specs2 测试。这看起来不错,除了我的一项测试涉及从文件读取和写入,因此会意外失败,例如见下文。

之外,还有更好的选择吗?

  1. 除了将所有测试设置为串行运行、
  2. 为每个测试使用单独的文件名和拆卸
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
  }
}

I've noticed that SBT is running my specs2 tests in parallel. This seems good, except one of my tests involves reading and writing from a file and hence fails unpredictably, e.g. see below.

Are there any better options than

  1. setting all tests to run in serial,
  2. using separate file names and tear-downs for each test?
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
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

梦情居士 2024-12-20 16:48:51

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

您仍然可以声明,对于给定的规范,示例必须按顺序执行。为此,您只需添加 sequential 到规范的开头:

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

  sequential

  "WriteAndRead" should {
   ...
  }
}

In addition to that is written about sbt above, you must know that specs2 runs all the examples of your specifications concurrently by default.

You can still declare that, for a given specification, the examples must be executed sequentially. To do that, you simply add sequential to the beginning of your specification:

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

  sequential

  "WriteAndRead" should {
   ...
  }
}
输什么也不输骨气 2024-12-20 16:48:51

套件的固定测试顺序可能会导致测试用例的相互依赖性和维护负担。

我更愿意在不接触文件系统的情况下进行测试(无论是业务逻辑还是序列化代码),或者如果这是不可避免的(至于测试与文件提要的集成),那么将使用创建临时文件:

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

Fixed sequence of tests for suites can lead to interdependency of test cases and burden in maintenance.

I would prefer to test without touching the file system (no matter either it is business logic or serialization code), or if it is inevitable (as for testing integration with file feeds) then would use creating temporary files:

// Create temp file.
File temp = File.createTempFile("pattern", ".suffix");
// Delete temp file when program exits.
temp.deleteOnExit();
自控 2024-12-20 16:48:51

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

The wiki link Pablo Fernandez gave in his answer is pretty good, though there's a minor error in the example that might throw one off (though, being a wiki, I can and did correct it). Here's a project/Build.scala that actually compiles and produces the expected filters, though I didn't actually try it out with tests.

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"
      )
}
热风软妹 2024-12-20 16:48:51

似乎还有第三种选择,即将串行测试分组在一个配置中,并单独运行它们,同时并行运行其余测试。

检查此 wiki,查找“并行执行应用程序”

There seems to be a third option, which is grouping the serial tests in a configuration and running them separately while running the rest in parallel.

Check this wiki, look for "Application to parallel execution".

你怎么这么可爱啊 2024-12-20 16:48:51

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

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

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

Other answers explained how to use make them run sequential.

While they're valid answers, in my opinion it's better to change your tests to let them run in parallel. (if possible)

In your example - use different files for each test.
If you have DB involved - use different (or random) users (or whatever isolation you can)
etc ...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文