Data-layer tests usually start with a page of setup: a DataSource, schema scripts, wiring. Storm's test module reduces that to one annotation, and then lets you assert something most stacks cannot: the SQL itself.
Test repository and query code against a real database schema, fast enough to run on every build, without booting a dependency-injection container or writing connection plumbing in every test class.
@StormTest creates an in-memory H2 database, runs your schema and data scripts, and injects test method parameters. No Spring context, no base class, no manual setup:
@StormTest(scripts = ["/schema.sql", "/data.sql"]) class OwnerRepositoryTest { @Test fun `finds owners by last name`(orm: ORMTemplate) { val owners = orm.entity<Owner>().findAll(Owner_.lastName eq "Smith") owners.size shouldBe 2 } }
Parameters resolve by type: ORMTemplate, DataSource, SqlCapture, or any type with a static of(DataSource) factory. Each test starts from the scripted state, and the whole cycle runs in milliseconds because nothing heavier than JUnit is involved.
A test that only checks results can pass while the query underneath quietly degrades. SqlCapture records every statement a block generates, with its operation type and bound parameters, so query shape becomes an assertion:
@Test fun `pet list loads owners in the same query`(orm: ORMTemplate, capture: SqlCapture) { val pets = capture.execute { orm.entity<Pet>().findAll() } pets.forEach { render(it.owner.lastName) } // walk the graph freely capture.count(Operation.SELECT) shouldBe 1 // an added query fails the build capture.count(Operation.UPDATE) shouldBe 0 // reads stay reads }
This turns performance properties into regression tests: "no N+1", "this service only reads", "the batch runs one statement" all become one-line assertions that fail the build when violated.
H2 keeps the loop fast, but dialect-specific behavior deserves the real database. @StormTest picks up a static dataSource() method, which is exactly the hook Testcontainers needs:
@StormTest(scripts = ["/schema-postgres.sql", "/data.sql"]) @Testcontainers class PostgresOwnerTest { companion object { @Container val postgres = PostgreSQLContainer("postgres:latest") @JvmStatic fun dataSource(): DataSource = PGSimpleDataSource().apply { setUrl(postgres.jdbcUrl) user = postgres.username password = postgres.password } } // the same tests now run against real PostgreSQL }
Scripts and parameter injection work unchanged, so the fast H2 suite and the thorough PostgreSQL suite share their test code.
The reference documentation covers the mechanics in depth: