3

我正在尝试将旧数据库测试套件从 Specs 迁移到Specs2。然而,Specs2 以一种奇怪的顺序运行测试(从我的角度来看),这会破坏测试,因为它们会更改数据库状态,并运行某些代码两次。

在下面找到测试的简化版本。据我了解,测试应该按以下顺序运行:(因为我已经指定了顺序):
! 222,,但是! 333! 444
实际发生的是它们按以下顺序执行:
! 333,,! 222! 444

以下是测试:

object IncludedSpec extends Specification {
  sequential
  println("sssstart")

  "IncludedSpec" should {

    println("..222")
    "context free block" >> {
      println("....! 222 the first test")
      1 === 1
    }

    var variableN = 0

    "block with context" >> {
      println("....333")
      "using one variable" >> {
        println("......! 333 doing some tests and setting variableN")
        variableN = 123
      }

      println("....444")
      "using it again" >> {
        println("......! 444 testing variableN")
        variableN === 123
      }
      println("....555")
    }
  }

  println("eeeend")
}

这是所有println输出:

sssstart
eeeend
sssstart
eeeend
..222
....333
......! 333 doing some tests and setting variableN
....444
....555
....! 222 the first test
......! 444 testing variableN

还有我的两个问题:

  1. 为什么不! 222先执行?

  2. 怎么可能sssstart eeeend输出两次?规范是一个对象并且没有创建两次?

奇怪的是,如果我从测试中移除副作用——即移除variableN并将测试主体替换为ok——测试以正确的顺序运行。

版本详细信息:我正在使用 Paly Framework 2.1-SNAPSHOT(2012 年 10 月 28 日的版本 203df0e)和 Scala 2.10.0-RC1 运行这些测试。我认为Play捆绑的Specs2版本是1.12版本,因为该inline方法可用,并且是在1.12(-SNAPSHOT)中添加的,参见https://github.com/etorreborre/specs2/issues/87并且没有后Specs2 版本。

(哦,如果你认为我应该完全重写测试,那么看看这个问题:How design a Specs2 database test, with interdependent tests?

4

2 回答 2

10

最初,在 specs2 中,您可以创建以下内容:

1 - 一个例子in"this is an example" in { 1 must_== 1 }

2 - 一个例子>>"this is an example" >> { 1 must_== 1 }

3 - 一个例子块>>

"this system should" >> {
  "do something" >> ok
  "do something else" >> ok
}

重要的是,它in仅用于示例,并接受任何可以转换为Result. 另一方面>>,可用于示例和示例组(具有统一的嵌套样式),因此它接受类型ExampleResult.

现在,当您想要执行以下操作时,事情开始变得有点复杂:

1 - 用于foreach创建一组示例

"this group has 5 examples" in {
  (1 to 5) foreach { i => "example "+i >> ok }
}

2 - 用于foreach创建一组期望

"this example has 5 expectations" in {
  (1 to 5) foreach { i => i must_== i }
}

问题是,这两个表达式foreach都有 type Unit。但他们正在做两件非常不同的事情!第一个是构建示例,因此需要立即评估此表达式以构建Specification. 第二个是创建 an 的主体Example并将稍后执行(或者如果示例被过滤掉,则可能永远不会执行)。两件事,使用相同的 operator >>,是行不通的。

所以决定这>>(block: =>Unit)意味着“这通过副作用构建了一组示例”,并且in(expectations: =>Unit)意味着“这构建了一个Example可能具有期望的主体,这将是一个副作用。

现在,当这更好地解释了为什么您的打印语句看到奇怪的顺序时:

..222
....333
......! 333 doing some tests and setting variableN
....444
....555

首先打印,因为它们包含在 type 块中Unit,解释为示例组。

和 :

....! 222 the first test
......! 444 testing variableN

因此会打印出来,因为它们位于 type 的块中MatchResult[_],也就是说,它们被视为示例的主体。

我同意这是令人困惑的,我希望这种解释能带来一些关于为什么事情会这样的观点。当然,另一个教训是“副作用是偷偷摸摸的,因为它们不会告诉你它们到底在做什么”。

因此,一般的 specs2 提示是始终以适当的值结束您的块(除非您使用foreach上面示例中所示的构造)。例如,ok在您进行变量赋值的块的末尾添加应该可以解决您的问题。

于 2012-11-02T10:40:32.823 回答
0

(更新:这不适用于我真正相当大的 Spec。它仍然以看似随机的顺序执行,Specs2 似乎从中间的某个地方开始。但是当我在一个可能有 10 个的较小的 Spec 上进行下面提到的替换时或 20 个示例,它确实导致规范以正确的顺序执行。奇怪

这是部分解决方法,但不是真正的答案:

在测试片段之前使用in代替, 。>>然后以正确的顺序执行测试。(但sssstart eeeend仍然发生两次。)

所以这可以按预期工作:(我已将某些替换>>in

object IncludedSpec extends Specification {
  sequential
  println("sssstart")

  "IncludedSpec" should {

    println("..222")
    "context free block" in {   // not >>
      println("....! 222 the first test")
      1 === 1
    }

    var variableN = 0

    "block with context" >> {
      println("....333")
      "using one variable" in {   // not >>
        println("......! 333 doing some tests and setting variableN")
        variableN = 123
      }

      println("....444")
      "using it again" in {   // not >>
        println("......! 444 testing variableN")
        variableN === 123
      }
      println("....555")
    }
  }

  println("eeeend")
}

我的困惑想法如下:

1.

我认为和之间的区别在于in>>只能
in出现在测试片段(即一个{... x must_== y ... }块)之前。但
>>可以出现在整个示例之前(带有嵌套的测试片段)和测试片段之前。

所以当我写的时候>>,Specs2 并不直接知道会发生什么。我不知道为什么这会改变测试执行顺序。除非 Specs2 需要……在{ ...}块内执行一些代码来找出答案?但是 Specs2 不使用反射吗?这感觉很奇怪。

2.

如果测试完全没有副作用(在这种情况下,更新 no shared vars)。或者,如果所有测试都有副作用。然后,据我所知,它们以正确的顺序执行。但是,如果某些测试除了读取shared之外没有副作用var,那么它们似乎是在写入 a 的测试之后var执行的。(如果你使用>>而不是in。)这也感觉很奇怪。无论如何,我想我很快就会开始将我的测试移植到 Specs2 并替换>>in:-)

于 2012-11-01T17:02:43.707 回答