Skip to content

feat: run @StormTest and @DataStormTest against a real database with Testcontainers - #509

Merged
zantvoort merged 4 commits into
mainfrom
feat/test-database-containers
Aug 16, 2026
Merged

feat: run @StormTest and @DataStormTest against a real database with Testcontainers#509
zantvoort merged 4 commits into
mainfrom
feat/test-database-containers

Conversation

@zantvoort

Copy link
Copy Markdown
Collaborator

Fixes #502

What

@StormTest and @DataStormTest gain database and image attributes that run the test on the database the application deploys on, in a Testcontainers-managed container, without any container wiring in the test class:

@StormTest(database = POSTGRESQL, scripts = {"/schema.sql", "/data.sql"})
class VisitRepositoryTest {

    @Test
    void findsVisitsByPet(ORMTemplate orm) {
        // running against a real PostgreSQL
    }
}
  • st.orm.test.TestDatabase: H2 (default), POSTGRESQL, MYSQL, MARIADB, MSSQL_SERVER, ORACLE, each with a pinned default image (postgres:17, mysql:8.4, mariadb:11.8, mcr.microsoft.com/mssql/server:2022-latest, gvenzl/oracle-free:23-slim-faststart). Majors are pinned, patch levels float. image names another tag or another distribution of the database (pgvector/pgvector:pg17).
  • DatabaseContainer: JVM-wide registry keyed by database and image; the container starts on first use and is shared by every test class of the run. createDatabase() provisions a fresh database inside the container per test class (JUnit) or per Spring context (slice): a new catalog on PostgreSQL, MySQL, MariaDB and SQL Server, a new user on Oracle, dropped when the class or context completes. That is what makes reuse safe beyond per-test rollback: scripts run into an empty database with no drop guards, and classes never observe each other's tables. Exposed publicly (TestDatabase.POSTGRESQL.container()) for setups outside the annotations.
  • Testcontainers is a provided dependency of storm-test. Only JdbcContainers references Testcontainers types and it is loaded after a Class.forName check, so a missing module or driver fails with add org.testcontainers:postgresql in test scope rather than a NoClassDefFoundError; tests on H2 pull in nothing new. database combined with url, a dataSource() factory, or image without a container database fails with an ExtensionConfigurationException naming the conflict.
  • @DataStormTest: a second ContextCustomizerFactory provisions the database, points spring.datasource.url/username/password at it, sets spring.test.database.replace=none and, unless the application configures it, spring.sql.init.mode=always so schema.sql initializes the container database as it does the embedded one. The customizer is part of the context cache key. storm-spring-boot-test-autoconfigure now depends on storm-test, so both annotations share one container within a JVM.
  • Docs: docs/testing.md (new section, attribute table), docs/spring-integration.md, docs/ai.md; CHANGELOG entry.

Notes

  • Testcontainers splits org.testcontainers.containers across its jars, so it can never be requires static from a module descriptor. storm-test compiles it from the classpath with --add-reads storm.test=ALL-UNNAMED (compiler and javadoc), commented in module-info.java.
  • The MySQL default is the 8.4 LTS line rather than the current lts tag (9.7): Testcontainers 1.21.3's bundled my.cnf sets innodb_log_file_size, which MySQL 9.x rejects, so 9.7 and latest fail to start under the Boot 3.5 BOM's Testcontainers.
  • SQL Server's EULA is not accepted on the user's behalf; Testcontainers' container-license-acceptance.txt mechanism applies and is documented.
  • storm-test's tests now need Docker: real container tests for all five databases (lifecycle, reuse across classes, fresh database per class, image override, configuration conflicts, missing-dependency message via an isolated class loader), plus a PostgreSQL slice test. Locally the whole storm-test suite runs in about 50s with images cached.
  • Left for follow-ups: aligning the dialect modules' own test images with these defaults (CI pulls both sets), the website testing tutorial (update once 1.14.0 ships), and a database parameter for Ktor's testStormApplication.

…Testcontainers

A database attribute on both annotations starts a Testcontainers-managed
container of the named database once per JVM and shares it across the test
classes of the run; each class, or Spring context, receives a freshly created
database inside the container, so scripts run against an empty database as
they do on H2 and classes never observe each other's tables. The image is
settable and defaults to a pinned version. Testcontainers stays out of
storm-test's dependencies: a test naming a container database needs the
database's Testcontainers module and JDBC driver on the classpath and fails
with a message naming both when one is missing.

Fixes #502
…mark the new storm-test types

The testing guide splits the database section into dependencies, image
choice, container sharing and direct use; the dialects, Ktor and installation
pages point at the attribute instead of at docker-compose or a custom
DataSource; the Spring slice section lists what it sets and why.
TestDatabase keeps its container details in one nullable record and
DatabaseContainer's endpoint is nullable until started, as the null-marked
package requires.
@codecov

codecov Bot commented Aug 16, 2026

Copy link
Copy Markdown

@zantvoort
zantvoort merged commit cce4374 into main Aug 16, 2026
9 checks passed
@zantvoort
zantvoort deleted the feat/test-database-containers branch August 16, 2026 08:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Run @StormTest against a real database with Testcontainers

1 participant