scala – 如何使用相互依赖的测试设计Specs2数据库测试?
|
是否有一些首选方法来设计
Specs2测试,大量测试取决于之前测试的结果?
下面,您将找到我当前的测试套件.我不喜欢测试片段之间的变量.然而,它们是“需要的”,因为一些测试生成后续测试重用的ID号. >我是否应该在Specs2上下文中存储ID号,或者创建一个包含所有可变状态的单独Object?并且只在测试对象中放置测试片段?还是有一些更好的方法? . object DatabaseSpec extends Specification {
sequential
"The Data Access Object" should {
var someId = "" // These var:s feels error prone,is there a better way?
"save an object" >> {
someId = database.save(something)
someId must_!= ""
// I'd like to cancel the remaining tests,below,at this "depth",// if this test fragmen fails. Can I do that?
// (That is,cancel "load one object","list all objects",etc,below.)
}
"load one object" >> {
anObject = database.load(someId)
anObject.id must_== someId
}
"list all objects" >> {
objs = database.listAll()
objs.find(_.id == someId) must beSome
}
var anotherId = ""
...more tests that create another object,and
...use both `someId` and `anotherId`...
var aThirdId = ""
...tests that use `someId`,`anotherId` and `aThirdId...
}
"The Data Access Object can also" >> {
...more tests...
}
}
解决方法
您的问题有两个部分:使用vars存储中间状态,并在一个失败时停止示例.
1 – 使用vars 在使用可变规范时,有一些使用变量的替代方法. 您可以使用延迟val来表示流程的步骤: 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/Then编写规范的方式,但在Given和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
}
}
我要做的改进是: >捕获失败异常,将其报告为失败而非错误 在这种情况下,它应该足够写: 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]对象可以从String =>创建MatchResult [T]因为MatchResult [T]对象已经包含类型为T的值,它将成为“Given”. 2 – 在失败的示例后停止执行 使用隐式的WhenFail Around上下文肯定是做你想要的最好的方法(除非你按照G / W / T例子上面所示的期望描述). 关于步骤的注意事项(stepOnFail = true) 如果前一个并发示例块中的一个示例失败,则步骤(stepOnFail = true)通过中断以下示例来工作.但是,当您使用顺序时,前一个块仅限于一个示例.因此你所看到的.实际上我认为这是一个错误,所有剩下的例子都不应该执行,无论你是否使用顺序.所以请继续关注本周末即将到来的修复工作. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
