您的问题有两个部分:使用 vars 存储中间状态,并在失败时停止示例。
1 - 使用变量
在使用可变规范时,有一些使用 vars 的替代方法。
您可以使用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)
另一种可能需要 2 个小的改进,是使用Given/When/ThenGiven
编写规范的方式,但在内部和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
}
Given[T]
可以从 a 创建对象,因为String => MatchResult[T]
该MatchResult[T]
对象已经包含 type 的值T
,这将成为“给定”。
2 - 示例失败后停止执行
使用隐式WhenFail
Around
上下文当然是做你想做的事的最好方法(除非你使用上面 G/W/T 示例所示的期望描述)。
注意事项step(stepOnFail = true)
如果上一个并发示例块中的一个示例失败,step(stepOnFail = true)
则通过中断以下示例来工作。但是,当您使用时,前一个块仅限于一个示例。因此,您所看到的。实际上,我认为这是一个错误,并且无论您是否使用顺序,都不应该执行所有剩余的示例。因此,请继续关注本周末即将推出的修复。sequential