Pull to refresh

Comments 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. Запуск тестов осуществлялся меньше секунды.

Sign up to leave a comment.