您的问题有两部分:使用变量来存储中间状态,并在示例失败时停止示例。
1 - 使用变量
使用可变规范时,有一些替代方法可以替代变量。
您可以使用lazy vals
代表您的流程的步骤:
object DatabaseSpec extends mutable.Specification {
sequential
"The Data Access Object" should {
lazy val id1 = database.save(Entity(1))
lazy val loaded = database.load(id1)
lazy val list = database.list
"save an object" >> { id1 === 1 }
"load one object" >> { loaded.id === id1 }
"list all objects" >> { list === Seq(Entity(id1)) }
}
object database {
def save(e: Entity) = e.id
def load(id: Int) = Entity(id)
def list = Seq(Entity(1))
}
case class Entity(id: Int)
}
由于这些值是惰性的,因此只有在执行示例时才会调用它们。
如果您准备好更改当前规范的结构,您还可以使用最新的 1.12.3-SNAPSHOT 并将所有这些小期望分组到一个示例中:
"The Data Access Object provides a save/load/list api to the database" >> {
lazy val id1 = database.save(Entity(1))
lazy val loaded = database.load(id1)
lazy val list = database.list
"an object can be saved" ==> { id1 === 1 }
"an object can be loaded" ==> { loaded.id === id1 }
"the list of all objects can be retrieved" ==> {
list === Seq(Entity(id1))
}
}
如果这些期望中的任何一个失败,那么其余的将不会被执行,并且您将收到如下失败消息:
x The Data Access Object provides a save/load/list api to the database
an object can not be saved because '1' is not equal to '2' (DatabaseSpec.scala:16)
另一种可能性是使用给定/何时/然后 http://etorreborre.github.com/specs2/guide/org.specs2.guide.Structure.html#G/W/T编写规范但在内部使用“抛出”期望的方式Given
and When
脚步。正如您在用户指南中所看到的,Given/When/Then
步骤从字符串中提取数据并将键入的信息传递给下一步Given/When/Then
:
import org.specs2._
import specification._
import matcher.ThrownExpectations
class DatabaseSpec extends Specification with ThrownExpectations { def is =
"The Data Access Object should"^
"save an object" ^ save^
"load one object" ^ load^
"list all objects" ^ list^
end
val save: Given[Int] = groupAs(".*") and { (s: String) =>
database.save(Entity(1)) === 1
1
}
val load: When[Int, Int] = groupAs(".*") and { (id: Int) => (s: String) =>
val e = database.load(id)
e.id === 1
e.id
}
val list: Then[Int] = groupAs(".*") then { (id: Int) => (s: String) =>
val es = database.list
es must have size(1)
es.head.id === id
}
}
我要做的改进是:
- 捕获失败异常,将其报告为失败而不是错误
- 消除使用的必要性
groupAs(".*") and
当无法从字符串描述中提取任何内容时。
在这种情况下,编写以下内容就足够了:
val save: Given[Int] = groupAs(".*") and { (s: String) =>
database.save(Entity(1)) === 1
1
}
另一种可能性是允许直接写入:
val save: Given[Int] = groupAs(".*") and { (s: String) =>
database.save(Entity(1)) === 1
}
where a Given[T]
对象可以从 a 创建String => MatchResult[T]
因为MatchResult[T]
对象已包含类型的值T
,这将成为“给定”。
2 - 失败示例后停止执行
使用隐式WhenFail
Around
上下文当然是做你想做的事情的最佳方式(除非你遵循 G/W/T 示例上方所示的期望描述)。
Note on step(stepOnFail = true)
The step(stepOnFail = true)
如果有一个例子,则通过中断以下示例来工作在前面的并发示例块中失败的。但是,当您使用sequential
,前一个块仅限于一个示例。这就是你所看到的。实际上,我认为这是一个错误,无论您是否使用顺序,所有剩余的示例都不应该执行。因此,请继续关注本周末即将推出的修复程序。