Packages
testcontainers
1.11.5
2.3.1
2.3.0
2.2.0
2.1.0
2.1.0-rc1
2.0.0
2.0.0-rc3
2.0.0-rc2
2.0.0-rc
1.14.1
1.14.0
1.13.5
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.8
1.11.7
1.11.6
1.11.5
1.11.4
1.11.3
1.11.2
1.11.1
1.11.0
1.10.5
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.0
1.8.4
1.8.3
1.8.2
1.8.1
1.8.0
1.7.0
1.6.0
1.5.1
1.5.0
1.4.2
1.4.1
1.4.0
1.3.1
1.3.0
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.3
1.1.2
1.1.1
1.1.0
1.0.1
1.0.0
1.0.0-beta.1
1.0.0-beta
0.9.3
0.9.2
0.9.1
0.9.0
Testcontainers is an Elixir library that supports ExUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/testcontainers/test.ex
defmodule Mix.Tasks.Testcontainers.Test do
use Mix.Task
alias Testcontainers.PostgresContainer
alias Testcontainers.MySqlContainer
@shortdoc "Runs tests with a Postgres container"
def run(args) do
Enum.each([:tesla, :hackney, :fs, :logger], fn app ->
{:ok, _} = Application.ensure_all_started(app)
end)
{:ok, _} = Testcontainers.start_link()
{opts, _, _} =
OptionParser.parse(args,
switches: [
database: :string,
watch: [:string, :keep]
]
)
database = opts[:database] || "postgres"
folder_to_watch = Keyword.get_values(opts, :watch)
if Enum.empty?(folder_to_watch) do
IO.puts("No folders specified. Only running tests.")
run_tests_and_exit(database)
else
check_folders_exist(folder_to_watch)
run_tests_and_watch(database, folder_to_watch)
end
end
defp check_folders_exist(folders) do
Enum.each(folders, fn folder ->
unless File.dir?(folder) do
raise("Folder does not exist: #{folder}")
end
end)
end
@spec run_tests_and_exit(String.t()) :: no_return()
defp run_tests_and_exit(database) do
{container, env} = setup_container(database)
exit_code = run_tests(env)
Testcontainers.stop_container(container.container_id)
System.halt(exit_code)
end
defp run_tests_and_watch(database, folders) do
{container, env} = setup_container(database)
Enum.each(folders, fn folder ->
:fs.start_link(String.to_atom("watcher_" <> folder), Path.absname(folder))
:fs.subscribe(String.to_atom("watcher_" <> folder))
end)
run_tests(env)
loop(env, container)
end
defp setup_container(database) do
case database do
"postgres" ->
{:ok, container} =
Testcontainers.start_container(
PostgresContainer.new()
|> PostgresContainer.with_user("test")
|> PostgresContainer.with_password("test")
|> PostgresContainer.with_reuse(true)
)
port = PostgresContainer.port(container)
{container, create_env(port)}
"mysql" ->
{:ok, container} =
Testcontainers.start_container(
MySqlContainer.new()
|> MySqlContainer.with_user("test")
|> MySqlContainer.with_password("test")
|> MySqlContainer.with_reuse(true)
)
port = MySqlContainer.port(container)
{container, create_env(port)}
_ ->
raise("Unsupported database: #{database}")
end
end
defp create_env(port) do
[
{"DB_USER", "test"},
{"DB_PASSWORD", "test"},
{"DB_HOST", Testcontainers.get_host()},
{"DB_PORT", Integer.to_string(port)}
]
end
defp run_tests(env) do
case System.cmd("mix", ["test"], env: env, into: IO.stream(:stdio, :line)) do
{_, exit_code} ->
if exit_code == 0 do
IO.puts("Test process completed successfully")
else
IO.puts(:stderr, "Test process failed with exit code: #{exit_code}")
end
exit_code
end
end
defp loop(env, container) do
receive do
{_watcher_process, {:fs, :file_event}, {changed_file, _type}} ->
IO.puts("#{changed_file} was updated, waiting for more changes...")
wait_for_changes(env, container)
after
5000 ->
loop(env, container)
end
end
defp wait_for_changes(env, container) do
receive do
{_watcher_process, {:fs, :file_event}, {changed_file, _type}} ->
IO.puts("#{changed_file} was updated, waiting for more changes...")
wait_for_changes(env, container)
after
1000 ->
IO.ANSI.clear()
run_tests(env)
loop(env, container)
end
end
end