All right, after reading through Ecto’s documentation, the testing source code of multiple libraries and a great conversation with @michalmuskala on Slack today, and I now know a bit more.
Turns out that getting Ecto ready for testing isn’t hard, it just includes a few steps that might not be self-evident:
1) Add/alter elixirc_paths
and aliases
in your mix.exs
:
defmodule Myproject.Mixfile do
use Mix.Project
def project do
[app: :myproject,
version: "0.1.0",
elixir: "~> 1.4",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps(),
elixirc_paths: elixirc_paths(Mix.env),
aliases: aliases()
]
end
# ...
# Ensures `test/support/*.ex` files are read during tests
def elixirc_paths(:test), do: ["lib", "test/support"]
def elixirc_paths(_), do: ["lib"]
defp aliases do
[
# Ensures database is reset before tests are run
"test": ["ecto.create --quiet", "ecto.migrate", "test"]
]
end
end
As commented, these respectively ensure that the files in test/support
are compiled during testing, and that the database state is reset and created before the tests are run.
###2) Add the Repo file to be used during testing to test/support/repo.ex
(or another file in this folder):
defmodule Myproject.Repo do
use Ecto.Repo, otp_app: :myproject
end
3) In your config.exs
, uncomment the include_config "#{Mix.env}.exs"
line, and create a test.exs
file with the configuration settings you need, which include:
# Repos known to Ecto:
config :myproject, ecto_repos: [Myproject.Repo]
# Test Repo settings
config :myproject, Myproject.Repo,
adapter: Ecto.Adapters.Postgres,
username: "postgres",
password: "postgres",
database: "myproject_test",
hostname: "localhost",
poolsize: 10,
# Ensure async testing is possible:
pool: Ecto.Adapters.SQL.Sandbox
Most notably the last line, pool: Ecto.Adapters.SQL.Sandbox
, is important. It will allow your database tests to be run concurrently. Read more about the Ecto.Adapters.SQL.Sandbox here.
4) Add the following two lines to your tests_helper.exs
:
(or at least, ensure these lines are executed before your database-tests are being run)
{:ok, _pid} = Myproject.Repo.start_link
Ecto.Adapters.SQL.Sandbox.mode(Myproject.Repo, :manual)
5) Above your database-hitting tests, add the following setup code:
defmodule MyprojectTest do
use ExUnit.Case, async: true # Yes! concurrent tests are possible!
doctest Myproject
# Not required, but helpful for test brevity:
alias Myproject.Repo
setup do
# Explicitly get a connection before each test
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
end
# ... your tests here.
end
This should be enough to get you going. The Ecto generator tasks can be executed by prefixing them with MIX_ENV=test
to ensure the testing Repo is found, e.g. MIX_ENV=test mix ecto.gen.migration my_migration_name
.