Easy PostgreSQL testing with pgTAP and Nix
This article explains one way to get from zero to a psql
shell inside a local
PostgreSQL instance with pgTAP enabled. It’s based on the Nix shell
template article.
Intro
To recap, all you need is to install Nix
install Nix, declare your environment in a
shell.nix
file, and run nix-shell
to enter the environment. We’ll start from
the template shell.nix
in the original article:
let
pkgs = import (fetchTarball {
name = "nixos-23.05_2023-06-30";
url = "https://github.com/NixOS/nixpkgs/archive/b72aa95f7f096382bff3aea5f8fde645bca07422.tar.gz";
sha256 = "1ndnsfzff0jdxvjnjnrdm74x8xq2c221hfr7swdnxm7pkmi5w9q5";
}) { };
in
pkgs.mkShell { packages = [ pkgs.bashInteractive ]; }
Building it
(Feel free to skip this part if you just want the thrilling conclusion.)
How do we need to change the template to achieve this? We can split it into a
few easily manageable tasks. First, we’ll need to install PostgreSQL itself.
That means changing the packages
list to include pkgs.postgresql
. Then we
need a small Bash script to set up and connect to the database:
- Set up some directives to exit early in case of failure in any of the subsequent commands.
- Configure an “exit trap” to stop the database and remove the files1 when exiting the interactive database session below.
- Create the database cluster using the
initdb
command. - Start PostgreSQL itself using the
pg_ctl
command. - Create a test database using the
createdb
command. This lets us work on a completely blank database, rather than an internal database. - Open an interactive shell within the test database using the
psql
command.
The result so far:
let
pkgs = import (fetchTarball {
name = "nixos-23.05_2023-06-30";
url = "https://github.com/NixOS/nixpkgs/archive/b72aa95f7f096382bff3aea5f8fde645bca07422.tar.gz";
sha256 = "1ndnsfzff0jdxvjnjnrdm74x8xq2c221hfr7swdnxm7pkmi5w9q5";
}) { };
in
pkgs.mkShell {
packages = [
pkgs.bashInteractive
pkgs.postgresql
];
env.PGDATA = "${toString ./.pgdata}";
shellHook = ''
set -o errexit -o noclobber -o nounset
# Remove traces of running server when exiting this shell hook
cleanup() {
pg_ctl stop
rm --force --recursive "$PGDATA"
}
trap cleanup EXIT
# Create database cluster
initdb --auth-local=trust --auth-host=trust
# Start server
pg_ctl --log="$PGDATA/db.log" --options="-c unix_socket_directories='$PGDATA'" start
# Create test database
db_name=test
createdb "$db_name" --host="$PGDATA"
# Connect to database
psql --dbname="$db_name" --host="$PGDATA"
# Return from Bash after exiting psql
exit
'';
}
Now we just need one more ingredient, pgTAP itself. We need to install it in such a way that the PostgreSQL package is aware of it, and then we need to enable it on our test database.
The result
let
pkgs = import (fetchTarball {
name = "nixos-23.05_2023-06-30";
url = "https://github.com/NixOS/nixpkgs/archive/b72aa95f7f096382bff3aea5f8fde645bca07422.tar.gz";
sha256 = "1ndnsfzff0jdxvjnjnrdm74x8xq2c221hfr7swdnxm7pkmi5w9q5";
}) { };
in
pkgs.mkShell {
packages = [
pkgs.bashInteractive
(pkgs.postgresql.withPackages (postgresqlPackages: [ postgresqlPackages.pgtap ]))
];
env.PGDATA = "${toString ./.pgdata}";
shellHook = ''
set -o errexit -o noclobber -o nounset
# Remove traces of running server when exiting this shell hook
cleanup() {
pg_ctl stop
rm --force --recursive "$PGDATA"
}
trap cleanup EXIT
# Create database cluster
initdb --auth-local=trust --auth-host=trust
# Start server
pg_ctl --log="$PGDATA/db.log" --options="-c unix_socket_directories='$PGDATA'" start
# Create test database
db_name=test
createdb "$db_name" --host="$PGDATA"
# Enable pgTAP
psql --command="CREATE EXTENSION pgtap" --dbname="$db_name" --host="$PGDATA"
# Connect to database
psql --dbname="$db_name" --host="$PGDATA"
# Return from Bash after exiting psql
exit
'';
}
pgTAP is now available by simply running nix-shell
! To verify, try running a
trivial test suite:
SELECT * FROM no_plan();
SELECT is(2, 2);
SELECT * FROM finish();
Variants
As you can see, enabling other PostgreSQL extensions from
nixpkgs is just a matter of adding two
lines: postgresqlPackages.[…]
and the
psql --command="CREATE EXTENSION […]" --dbname="$db_name" --host="$PGDATA"
command.
It’s also possible to run ancient versions of PostgreSQL by installing it from an older version of nixpkgs. Click on any of the versions above to get detailed instructions.
-
If you want to keep the database files around after stopping PostgreSQL, simply delete the
rm […]
line in thecleanup
function. ↩