使用 Slick、specs2 和 Postgresql 进行 2.4 测试
Posted
技术标签:
【中文标题】使用 Slick、specs2 和 Postgresql 进行 2.4 测试【英文标题】:Play 2.4 tests with Slick, specs2 and Postgresql 【发布时间】:2015-07-02 23:26:16 【问题描述】:我想要的是使用与生产环境中相同的数据库引擎、相同的演进和配置来运行我的测试。我的数据库是 PostgreSQL 9.4,我使用 Slick 3.0.0 来访问它。
这里有问题:
在执行并行测试的情况下,我会在同一个数据库上同时运行多个演进。这会导致错误。 如果执行顺序测试,线程池会出现另一个错误。这里有详细信息。
我使用进化来为每个测试初始化数据库。为此,我准备了一个基本规范类:
class DatabaseSpecification extends PlaySpecification
protected val defaultAppBuilder =
new GuiceApplicationBuilder()
.configure(ConfigurationLoader.loadFirst("application.test.override.conf", "application.test.conf"))
protected def afterEach(app: Application) =
recreateDbSchema(app)
private def recreateDbSchema(app: Application) =
val dbConfig = DatabaseConfigProvider.get[JdbcProfile](app)
import dbConfig.driver.api._
val recreateSchema: DBIO[Unit] = DBIO.seq(
sqlu"drop schema public cascade",
sqlu"create schema public"
)
Await.ready(dbConfig.db.run(recreateSchema), 5 seconds)
abstract class DatabaseContext() extends WithApplication(defaultAppBuilder.build())
protected val injector = implicitApp.injector
override def around[T](t: => T)(implicit evidence$2: AsResult[T]): Result = super.around
try
t
finally
afterEach(implicitApp)
application.test.override.conf
是测试的配置文件。
在祖先规范中也有几个测试:
"save1 and query" in new DatabaseContext
// create new user
val accountRepo = injector.instanceOf[SystemUserRepo]
val user = new SystemUser(id = 0, login = "admin", passwordHash = "", role = Role.Administrator)
val futureUserId = accountRepo.create(user)
// check if user id is greater then zero
val userId = await(futureUserId)(5 second)
userId must be_>(0)
"second one1" in new DatabaseContext
1 mustEqual 1
如果我并行运行整个规范的测试(默认),则在其中一个测试中得到异常 Database 'default' is in an inconsistent state![An evolution has not been applied properly. Please check the problem and resolve it manually before marking it as resolved.]
@6mk338l87: Database 'default' is in an inconsistent state!
。
如果我按顺序运行class AccountRepositoryTest extends DatabaseSpecification sequential ...
会出现另一个异常
Task slick.backend.DatabaseComponent$DatabaseDef$$anon$2@2744dcae rejected from java.util.concurrent.ThreadPoolExecutor@16d0e521[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 3]
java.util.concurrent.RejectedExecutionException: Task slick.backend.DatabaseComponent$DatabaseDef$$anon$2@2744dcae rejected from java.util.concurrent.ThreadPoolExecutor@16d0e521[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 3]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at scala.concurrent.impl.ExecutionContextImpl$$anon$1.execute(ExecutionContextImpl.scala:136)
at slick.backend.DatabaseComponent$DatabaseDef$class.runSynchronousDatabaseAction(DatabaseComponent.scala:224)
at slick.jdbc.JdbcBackend$DatabaseDef.runSynchronousDatabaseAction(JdbcBackend.scala:38)
at slick.backend.DatabaseComponent$DatabaseDef$class.runInContext(DatabaseComponent.scala:201)
at slick.jdbc.JdbcBackend$DatabaseDef.runInContext(JdbcBackend.scala:38)
at slick.backend.DatabaseComponent$DatabaseDef$class.runInternal(DatabaseComponent.scala:75)
at slick.jdbc.JdbcBackend$DatabaseDef.runInternal(JdbcBackend.scala:38)
at slick.backend.DatabaseComponent$DatabaseDef$class.run(DatabaseComponent.scala:72)
at slick.jdbc.JdbcBackend$DatabaseDef.run(JdbcBackend.scala:38)
at repository.GenericCRUD$class.create(GenericCRUD.scala:50)
at repository.GenericCRUDImpl.create(GenericCRUD.scala:70)
at unit.repositories.AccountRepositoryTest$$anonfun$3$$anon$1.delayedEndpoint$unit$repositories$AccountRepositoryTest$$anonfun$3$$anon$1$1(AccountRepositoryTest.scala:39)
at unit.repositories.AccountRepositoryTest$$anonfun$3$$anon$1$delayedInit$body.apply(AccountRepositoryTest.scala:35)
它发生在val futureUserId = accountRepo.create(user)
线上。可悲的是,但我不知道为什么会引发第二个异常。顺便说一句,我当然更喜欢并行运行测试,但不知道如何实现它。
任何帮助将不胜感激!
【问题讨论】:
要在测试期间模拟任何 JDBC 环境,您可以查看Acolyte approach,它允许对持久层进行隔离单元测试。 恐怕这对我没有帮助,因为我的数据库中有 sql 函数。 一旦持久层的单元测试的目的不是测试数据库引擎和/或功能,而是测试与该数据库一起使用的应用程序代码,要求能够在其中定义这些测试一些 JDBC 连接接受相同的语句并返回相同的 JDBC 结果(结果集|更新计数|错误),就像它使用 DB.Acolyte 一样,不管语句是什么。 【参考方案1】:目前您应该使用sequential
运行数据库测试,否则它将失败。
线程池错误来自:
protected val defaultAppBuilder =
new GuiceApplicationBuilder()
.configure(ConfigurationLoader.loadFirst("application.test.override.conf", "application.test.conf"))
它应该是一个函数:
protected def defaultAppBuilder =
new GuiceApplicationBuilder()
.configure(ConfigurationLoader.loadFirst("application.test.override.conf", "application.test.conf"))
然后它将开始工作。我们也这样做,但是我们有一个抽象类,它有一个val application
,我们可以在其中配置东西,还有一个def app
,它只会做一个def app = application.build()
,这样我们仍然可以扩展GuiceApplictionBuilder。
目前我首先遇到了同样的错误,你肯定不希望每次测试都重复使用相同的应用程序。
所以尝试一下,你就有了一个非常好的游戏应用程序功能测试套件。
然而,我们唯一不做的是public drop schema
,我们依靠进化来通过清理它们来解决这个问题。如果它们错了,我们会立即看到错误。
【讨论】:
嗨克里斯蒂安!我已将 val 更改为 def 但毫无疑问地与以前的线程池有相同的错误。请告诉我你是如何在测试后运行进化下降的? 我的 Evolutions 以这种方式运行:override def after = val dbapi = app.injector.instanceOf[DBApi] Evolutions.cleanupEvolutions(dbapi.database(prefix))
,然后在您的测试类中添加一个 with AfterEach
。但是,这也适用于您的 DatabaseContext。
Christian,如果您不介意请给我看一下您的基本规格类。以上是关于使用 Slick、specs2 和 Postgresql 进行 2.4 测试的主要内容,如果未能解决你的问题,请参考以下文章
使用 scala play specs2 针对测试 mysql 数据库的演变
排除 specs2 作为 sbt 中 play 框架的传递依赖