gpt4 book ai didi

scala - 如何使用相互依赖的测试设计 Specs2 数据库测试?

转载 作者:行者123 更新时间:2023-12-04 15:52:52 27 4
gpt4 key购买 nike

是否有一些首选的方式来设计 Specs2测试,有很多测试依赖于以前的测试结果?

下面,您将找到我当前的测试套件。我不喜欢 var s 在测试片段之间。不过,它们是“需要的”,因为某些测试会生成 ID 号,随后的测试会重复使用这些 ID 号。

  • 我应该将 ID 号存储在 Specs2 上下文中,还是创建一个单独的对象来保存所有可变状态?并且只在规范对象中放置测试片段?或者有什么更好的方法?
  • 如果测试失败,我想取消相同深度的剩余测试。我可以让测试片段相互依赖吗? (我知道我可以取消单个测试片段中剩余的匹配器(通过使用可变测试或通过 orSkip),但是取消整个片段呢?)

  • .
    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 的替代方法。

    您可以使用 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/Then编写规范但在内部使用“抛出”期望的方式 GivenWhen脚步。正如您在用户指南中所见, 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]对象可以从 String => MatchResult[T] 创建因为 MatchResult[T]对象已包含类型为 T 的值,这将成为“给定”。

    2 - 在一个失败的例子之后停止执行

    使用隐式 WhenFail Around context 当然是做你想做的最好的方式(除非你按照 G/W/T 示例上面显示的期望描述)。

    关于 step(stepOnFail = true) 的注释
    step(stepOnFail = true)如果前一个并发示例块中的一个示例失败,则通过中断以下示例来工作。但是,当您使用 sequential 时,前一个块仅限于一个示例。因此你所看到的。实际上,我认为这是一个错误,无论您是否使用顺序,都不应该执行所有剩余的示例。因此,请继续关注本周末即将推出的修复程序。

    关于scala - 如何使用相互依赖的测试设计 Specs2 数据库测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13174893/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com