Packages

A small utility to create a temporary postgres server for your tests

Current section

Files

Jump to
pg_test_case lib pg_test_case.ex
Raw

lib/pg_test_case.ex

defmodule PgTestCase do
@moduledoc """
PgTestCase is a small utility to manage the data lifecycle during tests
The module creates a temporary postgres server for your test session, runs your Ecto
migrations, and finally deletes the postgres server after all the tests have completed.
This module is meant to be used with ExUnit and Ecto, and specifically Ecto backed by
postgres. It will restart the Ecto Repo with the temporary database configuration, so that
tests can use Ecto.Repo just like normal.
## Use
To get started, simply use the module and tell it about your application and Ecto.Repo. It will
in turn use ExUnit.Case, which allows you to define test cases using `setup` and/or `setup_all`.
In this example we will tell ExUnit.Case to run `:initdb` and `:migrations`, to start the temporary
database and run the migrations, respectively.
defmodule MyAppTest do
use PgTestCase,
otp_app: :my_app,
repo: MyApp.Repo
setup_all [:initdb, :migrations]
test "validates database model" do
assert true
end
end
It's advised to pass the test case functions to `setup_all`, so that PgTestCase does not have to
recreate the database for each individual test. After the tests complete, ExUnit.Case will call
the PgTestCase cleanup function, which will stop and delete the temporary database server.
"""
@doc false
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
@otp_app Keyword.fetch!(opts, :otp_app)
@repo Keyword.fetch!(opts, :repo)
@database Keyword.get(opts, :database, "postgres")
@user Keyword.get(opts, :user, "postgres")
@prefix Keyword.get(opts, :prefix)
use ExUnit.Case
require Logger
def initdb(_context) do
import PgTestCase.Utils
Logger.info("Creating database \"#{@database}\"")
dir = mkdtemp()
port = get_available_port()
initdb_cmd(dir, @user)
start_postgres(dir, port)
# Hijack the repo config
Application.put_env(@otp_app, @repo,
hostname: "127.0.0.1",
port: port,
username: @user,
database: @database
)
repo = @repo.start_link()
if match?({:error, {:already_started, _pid}}, repo) do
@repo.stop() # Assume a supervisor will restart the repo
end
unless is_nil(@prefix) do
@repo.query("CREATE SCHEMA IF NOT EXISTS \"#{@prefix}\";")
end
on_exit(fn ->
Logger.info("Cleaning up database \"#{@database}\"")
cleanup(dir)
end)
{:ok, postgres: %{
hostname: "127.0.0.1",
port: port,
database: @database,
username: @user
}}
end
def migrations(%{postgres: _postgres} = _context) do
Ecto.Migrator.run(@repo, :up, all: true, prefix: @prefix)
:ok
end
end
end
end