Scala 在每个测试函数之后运行不同的代码

Scala 在每个测试函数之后运行不同的代码,scala,playframework,Scala,Playframework,我正在编写与数据库交互的测试,并希望针对每个测试进行某种设置和拆卸。这就是我目前拥有的: "my test" in { // Use anorm to populate the database Db.withConnection { SQL"INSERT INTO ...".execute() // Do some tests foo must equal 1 bar must equal 2

我正在编写与数据库交互的测试,并希望针对每个测试进行某种设置和拆卸。这就是我目前拥有的:

"my test" in {

    // Use anorm to populate the database
    Db.withConnection { 

        SQL"INSERT INTO ...".execute()

        // Do some tests
        foo must equal 1
        bar must equal 2

        // Remove the test data
        SQL"DELETE FROM ...".execute()
    }
}

这种方法的问题是,如果其中一个测试失败,那么执行就会停止,测试数据永远不会从数据库中删除。

假设您使用的是specs2,您可以为每个测试创建一个
范围:

"my test" in DatabaseSetup {
  // Do some tests
  foo must equal 1
  bar must equal 2
}

trait DatabaseSetup extends org.specs2.mutable.Around {
  def around[T: org.specs2.execute.AsResult](t: => T) = {

    Db.withConnection {
      // Insert data
    }

    val result = org.specs2.execute.AsResult(t)

    Db.withConnection {
      // Remove data
    }

    result
  }
}
import anorm._
import org.scalatest.{BeforeAndAfter, DoNotDiscover}
import org.scalatestplus.play.{ConfiguredServer, PlaySpec}
import play.api.db.DB
import play.api.libs.json.Json
import play.api.libs.ws.WS
import play.api.test.Helpers._

@DoNotDiscover
class ExampleSpec extends PlaySpec with BeforeAndAfter with ConfiguredServer {

    val baseAddress =  s"http://localhost:$testServerPort/"
    var rowID: Int = _

    before {
        DB.withConnection { implicit connection =>
            rowID = SQL"INSERT INTO some_table (col1, col2) VALUES('v1', 'v2')".executeInsert(SqlParser.scalar[Int].single)
        }
    }

    after {
        DB.withConnection { implicit connection =>
            SQL"DELETE FROM some_table WHERE id = $rowID".execute()
        }
    }

    "GET my/endpoint/{id}" should {

        "return a 200" in {
            val address = baseAddress + s"my/endpoint/$rowID"

            val response = await(WS.url(address).get())
            response.status mustEqual OK
        }
    }
}

无论如何,这可能很难维护,而且这种结构可能会暴露出生产代码或测试代码的一些问题。您应该问问自己,为什么不能重用数据库设置。

@marcospereira的答案适用于使用
specs2
进行测试的情况,但我使用的是
PlaySpec
。下面是一个例子:

  • 在数据库中插入一行
  • 设置
    rowID
    ,以便它可以在其他任何地方使用
  • 根据
    rowID
    ,向服务器发出GET请求
  • 测试结果,
  • 从数据库中删除相应的行

    import anorm._
    import org.scalatest.{BeforeAndAfter, DoNotDiscover}
    import org.scalatestplus.play.{ConfiguredServer, PlaySpec}
    import play.api.db.DB
    import play.api.libs.json.Json
    import play.api.libs.ws.WS
    import play.api.test.Helpers._
    
    @DoNotDiscover
    class ExampleSpec extends PlaySpec with BeforeAndAfter with ConfiguredServer {
    
        val baseAddress =  s"http://localhost:$testServerPort/"
        var rowID: Int = _
    
        before {
            DB.withConnection { implicit connection =>
                rowID = SQL"INSERT INTO some_table (col1, col2) VALUES('v1', 'v2')".executeInsert(SqlParser.scalar[Int].single)
            }
        }
    
        after {
            DB.withConnection { implicit connection =>
                SQL"DELETE FROM some_table WHERE id = $rowID".execute()
            }
        }
    
        "GET my/endpoint/{id}" should {
    
            "return a 200" in {
                val address = baseAddress + s"my/endpoint/$rowID"
    
                val response = await(WS.url(address).get())
                response.status mustEqual OK
            }
        }
    }
    

  • 我的应用程序是一个RESTAPI,所以我只想通过在数据库中放入一些数据来测试端点,然后点击端点(我知道会发生什么,因为我刚刚放入了数据),然后删除这些数据,这样我就不会污染任何其他测试。有更好的方法吗?如果测试集中在一个资源上,我不明白为什么每个测试“方法”都需要不同的设置。我的意思是,如果你有
    PersonApiSpec
    CountryApiSpec
    LessonApiSpec
    ,等等,我想这会给你足够的隔离。当然,这个观点是基于我可以从这个问题中推断出来的。:-)无论如何,我的答案中的环绕特征可以用于这两种情况。