Testing with sqlx
Let's try to implement the "one database per test" approach with sqlx.
Spinning up a "physical" database
We need to have a "physical" database instance to create a dedicated logical database for each test.
We recommend using an ephemeral Docker container for this purpose. Containers are portable, easy to spin up and tear
down.
If you don't have Docker installed, go get it! You can find instructions here.
In our ideal setup, you'd just execute cargo test and the required setup (i.e. spinning up the container) would be
executed automatically. We are not quite there yet, though, so for now you'll have to run it manually:
docker run -p 5432:5432 \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_USER=postgres \
-e POSTGRES_DB=postgres \
--name test_db \
postgres:15
Configuring sqlx
For this section, we'll be using sqlx to interact with PostgreSQL.
One of the key features provided by sqlx is compile-time query validation: when you compile your project,
sqlx will check that all your queries are valid SQL and that they are compatible with your database schema.
This is done via their custom macros: at compile-time, they issue a statement against a live database to carry
out the validation.
For that reason, we need to provide sqlx with a connection string to said database.
The common approach is to define a .env file in the root of the project: sqlx will automatically read it and
use the value of the DATABASE_URL variable as the connection string. We'll stick to this approach.
sqlxexposes a few different macro variants, but we'll mostly be usingsqlx::query!.
#[sqlx::test]
sqlx itself embraces the "one database per test" approach and provides a custom test attribute, #[sqlx::test], to do
the heavy lifting for you.
You add an input parameter to your test function (e.g. pool: sqlx::PgPool) and sqlx will automatically create a new
database and pass a connection pool to your test.
You can find the list of injectable parameters in the
sqlx::testdocumentation.
Under the hood, this
is what sqlx does:
- It connects to the database specified in the
DATABASE_URLenvironment variable. - It creates a new database with a random name.
- (Optional) It runs all the migrations in the
migrationsdirectory. - It creates a connection pool to the new database.
- It passes the connection pool to your test function.
- It waits for the test to complete.
- It deletes the database.
Exercise
The exercise for this section is located in 06_database_isolation/01_sqlx_test