Комментарии 3
Последние 4 года 90% моих тестов такие и есть - интеграционные, с БД в тестконтейнере и запросами по ХТТП.
И так как я работаю по ТДД и запускаю тесты по нескольку раз в минуту, мне пришлось научиться делать такие тесты более быстрыми, чем тесты на моках.
Два оснонвых секрета:
1) Не использовать @DynamicPropertySource, потому что это приводит к инвалидации контекста в кэше и запуску контекста для каждого тест-кейса
2) Использовать RAM-диск для постгреса.
Вместо DynamicPropertySource я использую такой трюк:
@ContextConfiguration(
// ...
initializers = [TestContainerDbContextInitializer::class]
)
class TestContainerDbContextInitializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
override fun initialize(applicationContext: ConfigurableApplicationContext) {
// это небольшая функция-расширение, которая просто перетирает
applicationContext.overrideProperties(
"spring.datasource.username" to pgContainer.username,
// ...
)
}
}
А чтобы посадить Postgres на рам-диск - такой:
.withTmpFs(mapOf("/var" to "rw"))
.withEnv("PGDATA", "/var/lib/postgresql/data-no-mounted")
В результате, у меня на i7-8700, 32 RAM, SSD интеграционные тесты выполняются от 14мс при тесте с моками в 163 мс:
А в проекте со скрина, я пошёл ещё радикальнее - отказался от @SpringBootTest и запускаю приложание руками, а в локальной разработке сначала ищу предзапущенную БД:
val context: ConfigurableApplicationContext by lazy {
SpringApplicationBuilder(TestsConfig::class.java)
.profiles("test")
.build()
.run()
}
@Import(
QYogaApp::class,
BackgroundsConfig::class,
TestPasswordEncoderConfig::class,
TestDataSourceConfig::class,
TestMinioConfig::class,
FailingController::class
)
@Configuration
class TestsConfig
private const val DB_USER = "postgres"
private const val DB_PASSWORD = "password"
val jdbcUrl: String by lazy {
try {
val con = DriverManager.getConnection(
PROVIDED_DB_URL.replace("qyoga", DB_USER),
DB_USER,
DB_PASSWORD
)
log.info("Provided db found, recreating it")
con.prepareStatement(
"""
DROP DATABASE IF EXISTS qyoga;
CREATE DATABASE qyoga;
""".trimIndent()
)
.execute()
log.info("Provided db found, recreated")
PROVIDED_DB_URL
} catch (e: SQLException) {
log.info("Provided Db not found: ${e.message}")
pgContainer.jdbcUrl
}
}
Это позволяет сэкономить ещё пару секунд на инициализации тестов, что имеет существенное значение, когда ты делаешь зелёным один тест кейс.
Возможно, я что-то упустил из виду. Но набор зависимостей выглядит больше, чем "необходимый".
Есть подключение jUnit, при этом org.testcontainers:spock, а использование самого Spock не заметил. Да и вряд ли в такой материал его стоило бы добавлять.
Зависимость junit:junit на Maven Central 4.13.2 последней версии, которая была выпущена 3 года назад, потому что артефакт был перемещен: org.junit.jupiter:junit-jupiter-api
Аннотации от lombok наблюдаются, который не упоминается сам, да и не видно особого повода для их применения в такой подаче "отдельный элементов" кода.
Указывать @DirtiesContext, как необходимую аннотацию для тест-класса не самый полезный совет во многих случаях.
Возможно, материал может дать какие-то стартовые подсказки совсем новичкам, но без попытки все самостоятельно "прогуглить", может направить куда-то не туда.
А в чем проблема указать в application-test.yaml jdbc driver к testcontainers и соответствующий url? Тогда вся инициализация делается автоматически и один раз.
Общая проблема с медленным запуском все же остаётся. В моем предыдущем проекте я использовал JPA, H2 и Guice. Запуск тестов осуществлялся меньше секунды.
Как облегчить жизнь программисту при написании тестов