Available on crate features
model-mysql or model-postgres or model-sqlite only.Expand description
A database per test that leaves nothing behind.
TestDb hands out a DbPool (deref, or TestDb::pool for a clone
the app’s state can own), so a test builds its app on it and drives it
with TestClient as usual:
use rust_web_server::test_client::TestDb;
let db = TestDb::sqlite().migrations("migrations").await?;
db.execute("INSERT INTO notes (text) VALUES ('hi')", &[]).await?;
let app_state_pool = db.pool(); // cheap clone, same databaseHow each backend isolates a test:
- SQLite (
TestDb::sqlite): a fresh in-memory database, its own perTestDb, with the migrations applied. It disappears when the last clone of the pool is dropped. Nothing is shared between tests, so they run in parallel. - PostgreSQL / MySQL (
TestDb::postgres,TestDb::mysql, or the_from_envforms readingRWS_TEST_POSTGRES_URL/RWS_TEST_MYSQL_URL): the migrations are applied to the database for real (once per process and directory, serialized), then the pool is pinned to one connection that is inside a transaction from the moment it connects. The test’s writes stay in that transaction, which is never committed:TestDb::rollbackrolls it back, and dropping theTestDbcloses the pool, so the server rolls it back when the connection goes.
Limits of the transaction (the usual ones for transactional tests):
- Use a database of its own for tests: the schema (migrations) persists.
- One connection: code that holds two connections at once (two
DbTransactions alive together, a query while iterating another) waits for itself.DbPool::begin/transactionwork — sqlx turns them into savepoints inside the test’s transaction, so theircommitcommits nothing. - Raw
COMMIT/BEGINstatements, and on MySQL DDL (CREATE,ALTER,DROP,TRUNCATEcommit implicitly), do end the transaction: whatever ran before them stays. Put such statements in migrations. - On PostgreSQL a failed statement aborts the test’s transaction; every
later statement fails until the test ends. Run statements expected to
fail inside
DbPool::transaction(a savepoint) instead. - Concurrent tests on the same rows can block each other on row locks until one ends.
Structs§
- TestDb
- A database for one test; see the module docs.
- Test
DbBuilder - A
TestDbbeing set up: add migration directories, then.awaitit (or callbuild).