Проиграйте 2.4 теста с Slick, specs2 и Postgresql
Я хочу, чтобы мои тесты выполнялись с тем же механизмом базы данных, теми же эволюциями и конфигурацией, что и в производстве. Моя база данных - 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)
, К сожалению, я понятия не имею, почему выбрасывает второе исключение. Кстати, я предпочитаю проводить тесты параллельно, но не знаю, как этого добиться.
Любая помощь будет оценена!
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
мы полагаемся на эволюцию, чтобы исправить это, очистив их. Если они ошибаются, мы сразу видим ошибки.