在scalatest中创建和删除scala光滑表的前后异步



我试图找出一种方法,有异步beforeafter语句,下一个测试用例不运行,直到测试用例内部的动作完成。在我的例子中,它是在数据库

中创建和删除表。
  val table = TableQuery[BlockHeaderTable]
  val dbConfig: DatabaseConfig[PostgresDriver] = DatabaseConfig.forConfig("databaseUrl")
  val database: Database = dbConfig.db
  before {
    //Awaits need to be used to make sure this is fully executed before the next test case starts
    //TODO: Figure out a way to make this asynchronous 
    Await.result(database.run(table.schema.create), 10.seconds)
  }
  "BlockHeaderDAO" must "store a blockheader in the database, then read it from the database" in {
    //...
  }
  it must "delete a block header in the database" in {
    //...
  }
  after {
    //Awaits need to be used to make sure this is fully executed before the next test case starts
    //TODO: Figure out a way to make this asynchronous
    Await.result(database.run(table.schema.drop),10.seconds)
  }

是否有一个简单的方法,我可以删除这些Await调用我的beforeafter函数内?

不幸的是,@Jeffrey Chung的解决方案为我挂了(因为futureValue实际上在内部等待)。这是我最后做的:

import org.scalatest.{AsyncFreeSpec, FutureOutcome}
import scala.concurrent.Future
class TestTest extends AsyncFreeSpec /* Could be any AsyncSpec. */ {
  // Do whatever setup you need here.
  def setup(): Future[_] = ???
  // Cleanup whatever you need here.
  def tearDown(): Future[_] = ???
  override def withFixture(test: NoArgAsyncTest) = new FutureOutcome(for {
    _ <- setup()
    result <- super.withFixture(test).toFuture
    _ <- tearDown()
  } yield result)
}

下面是Dennis Vriend在他的slick-3.2.0-test项目中采用的测试方法。

首先定义一个dropCreateSchema方法。这个方法尝试创建一个表;如果尝试失败(例如,因为表已经存在),它将删除表,然后创建表:

def dropCreateSchema: Future[Unit] = {
  val schema = BlockHeaderTable.schema
  db.run(schema.create)
    .recoverWith {
      case t: Throwable =>
        db.run(DBIO.seq(schema.drop, schema.create))
    }
}
其次,定义一个createEntries方法,用一些示例数据填充表,以便在每个测试用例中使用:
def createEntries: Future[Unit] = {
  val setup = DBIO.seq(
    // insert some rows
    BlockHeaderTable ++= Seq(
      BlockHeaderTableRow(/* ... */),
      // ...
    )
  ).transactionally
  db.run(setup)
}

第三,定义一个initialize方法,它依次调用上述两个方法:

def initialize: Future[Unit] = for {
  _ <- dropCreateSchema
  _ <- createEntries
} yield ()

在测试类中,混合ScalaFutures性状。例如:

class TestSpec extends FlatSpec
  with Matchers
  with ScalaFutures
  with BeforeAndAfterAll
  with BeforeAndAfterEach {
  // ...
}

同样在测试类中,定义从FutureTry的隐式转换,并覆盖beforeEach方法以调用initialize:

implicit val timeout: Timeout = 10.seconds
implicit class PimpedFuture[T](self: Future[T]) {
  def toTry: Try[T] = Try(self.futureValue)
}
override protected def beforeEach(): Unit = {
  blockHeaderRepo.initialize // in this example, initialize is defined in a repo class
    .toTry recover {
      case t: Throwable =>
        log.error("Could not initialize the database", t)
    } should be a 'success
}
override protected def afterAll(): Unit = {
  db.close()
}

以上部分都准备好了,就不需要Await了。

你可以简化@Jeffrey Chung

简化的dropCreateSchema方法:

def dropCreateSchema: Future[Unit] = {
  val schema = users.schema
  db.run(DBIO.seq(schema.dropIfExists, schema.create))
}

同样在测试类中,我简化了调用initializebeforeEach方法。我删除了从FutureTry的隐式转换,并使用onComplete回调:

  override protected def beforeEach(): Unit = {
    initialize.onComplete(f => 
      f recover {
        case t: Throwable =>
          log.error("Could not initialize the database", t)
      } should be a 'success)
  }
  override protected def afterAll(): Unit = {
    db.close()
  }

最新更新