Scala 规格2整套服装前后的环境设置

Scala 规格2整套服装前后的环境设置,scala,amazon-dynamodb,spray,specs2,Scala,Amazon Dynamodb,Spray,Specs2,我正在为使用dynamodb的spray.io项目编写一些specc2集成测试。我使用sbt dynamodb将本地dynamodb加载到环境中。在运行测试之前,我使用以下模式加载表 trait DynamoDBSpec extends SpecificationLike { val config = ConfigFactory.load() val client = new AmazonDynamoDBClient(new DefaultAWSCredentialsProvider

我正在为使用dynamodb的spray.io项目编写一些specc2集成测试。我使用sbt dynamodb将本地dynamodb加载到环境中。在运行测试之前,我使用以下模式加载表

trait DynamoDBSpec extends SpecificationLike {

  val config = ConfigFactory.load()

  val client = new AmazonDynamoDBClient(new DefaultAWSCredentialsProviderChain())

  lazy val db = {
    client.setEndpoint(config.getString("campaigns.db.endpoint"))
    new DynamoDB(client)
  }


  override def map(fs: =>Fragments): Fragments =
    Step(beforeAll) ^ fs ^ Step(afterAll)

  protected def beforeAll() = {
    //load my tables
  }

  protected def afterAll() = {
    //delete my tables
  }
}
然后可以使用DynamoDBSpec扩展任何测试类,并创建表。在从多个测试类扩展DynamoDBSpec之前,这一切都可以正常工作,在此期间,它抛出ResourceInUseException:“无法创建预先存在的表”。原因是它们并行执行,因此它希望同时执行表创建

我试图通过在顺序模式下运行测试来克服它,但前后仍然是并行执行的


理想情况下,我认为最好是在整个套件运行之前创建表,而不是在每次规范类调用之前创建表,然后在整个套件完成后将其拆下。有人知道如何做到这一点吗?

有两种方法可以做到这一点

用一个物体 您可以使用对象来同步数据库的创建

object Database {
  lazy val config = ConfigFactory.load()

  lazy val client = 
    new AmazonDynamoDBClient(new DefaultAWSCredentialsProviderChain())

  // this will only be done once in
  // the same jvm
  lazy val db = {
    client.setEndpoint(config.getString("campaigns.db.endpoint"))
    val database = new DynamoDB(client)
    // drop previous tables if any 
    // and create new tables
    database.create...
    database
  }
}

// BeforeAll is a new trait in specs2 3.x
trait DynamoDBSpec extends SpecificationLike with BeforeAll {
  //load my tables
  def beforeAll = Database.db
}
正如您所看到的,在这个模型中,我们不会在规范完成时删除表(因为我们不知道是否已经执行了所有其他规范),我们只是在重新运行规范时删除表。这实际上是一件好事,因为这将帮助您调查任何失败

另一种在全局级别同步规范并最终正确清理规范的方法是使用规范链接

有链接 使用,您可以使用
链接创建规范之间的依赖关系。这意味着您可以定义一个“套件”规范,该规范将:

  • 启动数据库
  • 收集所有相关规范
  • 删除数据库
  • 比如说

    import org.specs2._
    import specification._
    import core.Fragments
    import runner.SpecificationsFinder
    
    // run this specification with `all` to execute
    // all linked specifications
    class Database extends Specification { def is =
      "All database specifications".title ^ br ^
      link(new Create).hide ^
      Fragments.foreach(specs)(s => link(s) ^ br) ^
      link(new Delete).hide
    
      def specs = specifications(pattern = ".*Db.*")
    } 
    
    // start the database with this specification
    class Create extends Specification { def is = xonly ^ 
      step("create database".pp)
    }
    
    // stop the database with this specification
    class Delete extends Specification {  def is = xonly ^
      step("delete database".pp)
    }
    
    // an example of a specification using the database
    // it will be invoked by the "Database" spec because 
    // its name matches ".*Db.*"
    class Db1Spec extends Specification { def is = s2"""
       test $db
       """
      def db = { println("use the database - 1"); ok }
    }
    class Db2Spec extends Specification { def is = s2"""
       test $db
       """
      def db = { println("use the database - 2"); ok }
    }
    
    运行时:

    sbt> test-only *Database* -- all
    
    你应该看到像这样的痕迹

    create database
    use the database - 1
    use the database - 2
    delete database 
    

    创建多个DBs是否昂贵?如果没有,我将为每个规范创建具有随机名称的数据库,并并行运行测试。您可以在
    之前使用
    上下文进行此操作。此示例使用内存中的MongoDB(Fongo),其中它为每个测试创建一个测试数据库:。看看测试助手类和其他测试的实现。如果你能以某种方式便宜地创建许多数据库,这一切都适用。是的,以前考虑过这一点。目前,表名是在配置中指定的。我想我必须重构我的代码才能将表名传递给更新函数。或者你可以为每个测试定制配置,其中它只是一个默认的HOCON配置,你已经有了
    ConfigFactory.load()
    ,具有如下表名的自定义覆盖:
    val updatedConfig=config.withValue(“你的.key”),ConfigValueFactory.fromAnyRef(“smthrandom”)
    .Sweet,这使它变得非常简单。这一技巧将在将来的更多规范测试中派上用场。抱歉,我被其他东西转移了。感谢链接示例,它起到了作用。