Current section
Files
Jump to
Current section
Files
src/dream_test@unit.erl
-module(dream_test@unit).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/dream_test/unit.gleam").
-export([describe/2, group/2, it/2, skip/2, before_all/1, before_each/1, after_each/1, after_all/1, with_tags/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Unit test DSL (no explicit context).\n"
"\n"
" This is the default DSL for most users: `describe` + `it` with **0‑argument**\n"
" test bodies.\n"
"\n"
" - Tests are written as `it(\"does something\", fn() { ... })`\n"
" - Hooks are also **0‑argument** functions (`before_each(fn() { ... })`)\n"
" - All hooks/tests return `Result(AssertionResult, String)` so you can abort\n"
" early with `Error(\"message\")` when prerequisites fail.\n"
"\n"
" This module builds a `dream_test/types.TestSuite(Nil)` under the hood.\n"
"\n"
" ## When should I use this module?\n"
"\n"
" - Use `dream_test/unit` for most unit tests.\n"
" - Use `dream_test/unit_context` only when you want a **real context value**\n"
" threaded through hooks and test bodies.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it}\n"
" import gleam/string\n"
"\n"
" pub fn tests() {\n"
" describe(\"String utilities\", [\n"
" it(\"trims whitespace\", fn() {\n"
" \" hello \"\n"
" |> string.trim()\n"
" |> should\n"
" |> be_equal(\"hello\")\n"
" |> or_fail_with(\"Should remove surrounding whitespace\")\n"
" }),\n"
" it(\"finds substrings\", fn() {\n"
" \"hello world\"\n"
" |> string.contains(\"world\")\n"
" |> should\n"
" |> be_equal(True)\n"
" |> or_fail_with(\"Should find 'world' in string\")\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
).
-file("src/dream_test/unit.gleam", 125).
?DOC(
" Create a top-level test suite.\n"
"\n"
" The returned value is what you pass to `runner.new([ ... ])`.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it}\n"
" import gleam/string\n"
"\n"
" pub fn tests() {\n"
" describe(\"String utilities\", [\n"
" it(\"trims whitespace\", fn() {\n"
" \" hello \"\n"
" |> string.trim()\n"
" |> should\n"
" |> be_equal(\"hello\")\n"
" |> or_fail_with(\"Should remove surrounding whitespace\")\n"
" }),\n"
" it(\"finds substrings\", fn() {\n"
" \"hello world\"\n"
" |> string.contains(\"world\")\n"
" |> should\n"
" |> be_equal(True)\n"
" |> or_fail_with(\"Should find 'world' in string\")\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `name`: the suite name (shown in reports)\n"
" - `children`: the suite contents (tests, groups, and hooks)\n"
"\n"
" ## Returns\n"
"\n"
" A `TestSuite(Nil)` you can pass to `runner.new([ ... ])`.\n"
).
-spec describe(binary(), list(dream_test@types:node_(nil))) -> dream_test@types:root(nil).
describe(Name, Children) ->
{root, nil, {group, Name, [], Children}}.
-file("src/dream_test/unit.gleam", 191).
?DOC(
" Create a nested group inside a suite.\n"
"\n"
" Groups provide structure (and hook scoping). Hooks declared in an outer group\n"
" apply to tests in inner groups.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, group, it}\n"
" import gleam/io\n"
"\n"
" pub fn tests() {\n"
" describe(\"Calculator\", [\n"
" group(\"addition\", [\n"
" it(\"adds small numbers\", fn() {\n"
" 2 + 3\n"
" |> should\n"
" |> be_equal(5)\n"
" |> or_fail_with(\"2 + 3 should equal 5\")\n"
" }),\n"
" it(\"adds negative numbers\", fn() {\n"
" -2 + -3\n"
" |> should\n"
" |> be_equal(-5)\n"
" |> or_fail_with(\"-2 + -3 should equal -5\")\n"
" }),\n"
" ]),\n"
" group(\"division\", [\n"
" it(\"integer division rounds toward zero\", fn() {\n"
" 7 / 2\n"
" |> should\n"
" |> be_equal(3)\n"
" |> or_fail_with(\"7 / 2 should equal 3\")\n"
" }),\n"
" ]),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `name`: the group name (shown in reports and in `runner.TestInfo.full_name`)\n"
" - `children`: nested tests/groups/hooks under this group\n"
"\n"
" ## Returns\n"
"\n"
" A `UnitNode` you include in a parent `describe`/`group` children list.\n"
).
-spec group(binary(), list(dream_test@types:node_(nil))) -> dream_test@types:node_(nil).
group(Name, Children) ->
{group, Name, [], Children}.
-file("src/dream_test/unit.gleam", 248).
?DOC(
" Define a single test case.\n"
"\n"
" - The body is **0-arg** (`fn() { ... }`)\n"
" - Return `Ok(...)` to indicate an assertion result\n"
" - Return `Error(\"message\")` to abort the test with a message\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it}\n"
" import gleam/io\n"
" import gleam/string\n"
"\n"
" pub fn tests() {\n"
" describe(\"String utilities\", [\n"
" it(\"trims whitespace\", fn() {\n"
" \" hello \"\n"
" |> string.trim()\n"
" |> should\n"
" |> be_equal(\"hello\")\n"
" |> or_fail_with(\"Should remove surrounding whitespace\")\n"
" }),\n"
" it(\"finds substrings\", fn() {\n"
" \"hello world\"\n"
" |> string.contains(\"world\")\n"
" |> should\n"
" |> be_equal(True)\n"
" |> or_fail_with(\"Should find 'world' in string\")\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `name`: the test name (shown in reports)\n"
" - `run`: a **0-argument** test body that returns `Result(AssertionResult, String)`\n"
"\n"
" ## Returns\n"
"\n"
" A `UnitNode` representing the test.\n"
).
-spec it(
binary(),
fun(() -> {ok, dream_test@types:assertion_result()} | {error, binary()})
) -> dream_test@types:node_(nil).
it(Name, Run) ->
{test, Name, [], unit, fun(_) -> Run() end, none, none}.
-file("src/dream_test/unit.gleam", 262).
-spec skipped_test_run(nil) -> {ok, dream_test@types:assertion_result()} |
{error, binary()}.
skipped_test_run(_) ->
{ok, assertion_skipped}.
-file("src/dream_test/unit.gleam", 325).
?DOC(
" Define a skipped test.\n"
"\n"
" `skip` has the same shape as `it` so you can easily switch a test between\n"
" running and skipped without rewriting the test body.\n"
"\n"
" The provided test body is preserved for that purpose, but it is **not\n"
" executed** while the test is skipped.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it, skip}\n"
" import gleam/io\n"
"\n"
" pub fn tests() {\n"
" describe(\"Skipping tests\", [\n"
" it(\"runs normally\", fn() {\n"
" 2 + 3\n"
" |> should\n"
" |> be_equal(5)\n"
" |> or_fail_with(\"2 + 3 should equal 5\")\n"
" }),\n"
" skip(\"not implemented yet\", fn() {\n"
" // This test is skipped - the body is preserved but not executed\n"
" 100 + 200\n"
" |> should\n"
" |> be_equal(300)\n"
" |> or_fail_with(\"Should add large numbers\")\n"
" }),\n"
" it(\"also runs normally\", fn() {\n"
" 0 + 0\n"
" |> should\n"
" |> be_equal(0)\n"
" |> or_fail_with(\"0 + 0 should equal 0\")\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `name`: the test name\n"
" - `run`: a **0-argument** function (accepted but never executed)\n"
"\n"
" ## Returns\n"
"\n"
" A `UnitNode` representing a skipped test (`AssertionSkipped`).\n"
).
-spec skip(
binary(),
fun(() -> {ok, dream_test@types:assertion_result()} | {error, binary()})
) -> dream_test@types:node_(nil).
skip(Name, Run) ->
Node = it(Name, Run),
case Node of
{test, Name@1, Tags, Kind, _, Timeout_ms, Source} ->
{test,
Name@1,
Tags,
Kind,
fun skipped_test_run/1,
Timeout_ms,
Source};
Other ->
Other
end.
-file("src/dream_test/unit.gleam", 434).
?DOC(
" Run once before any tests in the current suite/group.\n"
"\n"
" - Runs in a sandboxed process.\n"
" - If it returns `Error(\"message\")`, all tests under this scope become\n"
" `SetupFailed`.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_empty, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{\n"
" after_all, after_each, before_all, before_each, describe, it,\n"
" }\n"
" import gleam/io\n"
"\n"
" pub fn tests() {\n"
" describe(\"Database tests\", [\n"
" before_all(fn() {\n"
" // Start database once for all tests\n"
" start_database()\n"
" }),\n"
" before_each(fn() {\n"
" // Begin transaction before each test\n"
" begin_transaction()\n"
" }),\n"
" it(\"creates a record\", fn() {\n"
" []\n"
" |> should\n"
" |> be_empty()\n"
" |> or_fail_with(\"Placeholder test\")\n"
" }),\n"
" it(\"queries records\", fn() {\n"
" []\n"
" |> should\n"
" |> be_empty()\n"
" |> or_fail_with(\"Placeholder test\")\n"
" }),\n"
" after_each(fn() {\n"
" // Rollback transaction after each test\n"
" rollback_transaction()\n"
" }),\n"
" after_all(fn() {\n"
" // Stop database after all tests\n"
" stop_database()\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" fn start_database() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn stop_database() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn begin_transaction() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn rollback_transaction() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `setup`: a **0-argument** function that returns `Ok(Nil)` on success or `Error(message)` on failure\n"
"\n"
" ## Returns\n"
"\n"
" A `UnitNode` representing a `before_all` hook.\n"
).
-spec before_all(fun(() -> {ok, nil} | {error, binary()})) -> dream_test@types:node_(nil).
before_all(Setup) ->
{before_all, fun(_) -> case Setup() of
{ok, _} ->
{ok, nil};
{error, Message} ->
{error, Message}
end end}.
-file("src/dream_test/unit.gleam", 526).
?DOC(
" Run before each test in the current scope.\n"
"\n"
" - Runs in a sandboxed process.\n"
" - If it returns `Error(\"message\")`, that test becomes `SetupFailed` and the\n"
" body does not run.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_empty, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{\n"
" after_all, after_each, before_all, before_each, describe, it,\n"
" }\n"
" import gleam/io\n"
"\n"
" pub fn tests() {\n"
" describe(\"Database tests\", [\n"
" before_all(fn() {\n"
" // Start database once for all tests\n"
" start_database()\n"
" }),\n"
" before_each(fn() {\n"
" // Begin transaction before each test\n"
" begin_transaction()\n"
" }),\n"
" it(\"creates a record\", fn() {\n"
" []\n"
" |> should\n"
" |> be_empty()\n"
" |> or_fail_with(\"Placeholder test\")\n"
" }),\n"
" it(\"queries records\", fn() {\n"
" []\n"
" |> should\n"
" |> be_empty()\n"
" |> or_fail_with(\"Placeholder test\")\n"
" }),\n"
" after_each(fn() {\n"
" // Rollback transaction after each test\n"
" rollback_transaction()\n"
" }),\n"
" after_all(fn() {\n"
" // Stop database after all tests\n"
" stop_database()\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" fn start_database() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn stop_database() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn begin_transaction() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn rollback_transaction() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `setup`: a **0-argument** function that returns `Ok(Nil)` on success or `Error(message)` on failure\n"
"\n"
" ## Returns\n"
"\n"
" A `UnitNode` representing a `before_each` hook.\n"
).
-spec before_each(fun(() -> {ok, nil} | {error, binary()})) -> dream_test@types:node_(nil).
before_each(Setup) ->
{before_each, fun(_) -> case Setup() of
{ok, _} ->
{ok, nil};
{error, Message} ->
{error, Message}
end end}.
-file("src/dream_test/unit.gleam", 617).
?DOC(
" Run after each test in the current scope.\n"
"\n"
" This is useful for cleanup that must always run (even after assertion\n"
" failures).\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_empty, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{\n"
" after_all, after_each, before_all, before_each, describe, it,\n"
" }\n"
" import gleam/io\n"
"\n"
" pub fn tests() {\n"
" describe(\"Database tests\", [\n"
" before_all(fn() {\n"
" // Start database once for all tests\n"
" start_database()\n"
" }),\n"
" before_each(fn() {\n"
" // Begin transaction before each test\n"
" begin_transaction()\n"
" }),\n"
" it(\"creates a record\", fn() {\n"
" []\n"
" |> should\n"
" |> be_empty()\n"
" |> or_fail_with(\"Placeholder test\")\n"
" }),\n"
" it(\"queries records\", fn() {\n"
" []\n"
" |> should\n"
" |> be_empty()\n"
" |> or_fail_with(\"Placeholder test\")\n"
" }),\n"
" after_each(fn() {\n"
" // Rollback transaction after each test\n"
" rollback_transaction()\n"
" }),\n"
" after_all(fn() {\n"
" // Stop database after all tests\n"
" stop_database()\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" fn start_database() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn stop_database() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn begin_transaction() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn rollback_transaction() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `teardown`: a **0-argument** function that returns `Ok(Nil)` or `Error(message)`\n"
"\n"
" ## Returns\n"
"\n"
" A `UnitNode` representing an `after_each` hook.\n"
).
-spec after_each(fun(() -> {ok, nil} | {error, binary()})) -> dream_test@types:node_(nil).
after_each(Teardown) ->
{after_each, fun(_) -> Teardown() end}.
-file("src/dream_test/unit.gleam", 700).
?DOC(
" Run once after all tests in the current scope.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_empty, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{\n"
" after_all, after_each, before_all, before_each, describe, it,\n"
" }\n"
" import gleam/io\n"
"\n"
" pub fn tests() {\n"
" describe(\"Database tests\", [\n"
" before_all(fn() {\n"
" // Start database once for all tests\n"
" start_database()\n"
" }),\n"
" before_each(fn() {\n"
" // Begin transaction before each test\n"
" begin_transaction()\n"
" }),\n"
" it(\"creates a record\", fn() {\n"
" []\n"
" |> should\n"
" |> be_empty()\n"
" |> or_fail_with(\"Placeholder test\")\n"
" }),\n"
" it(\"queries records\", fn() {\n"
" []\n"
" |> should\n"
" |> be_empty()\n"
" |> or_fail_with(\"Placeholder test\")\n"
" }),\n"
" after_each(fn() {\n"
" // Rollback transaction after each test\n"
" rollback_transaction()\n"
" }),\n"
" after_all(fn() {\n"
" // Stop database after all tests\n"
" stop_database()\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" fn start_database() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn stop_database() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn begin_transaction() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" fn rollback_transaction() {\n"
" Ok(Nil)\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `teardown`: a **0-argument** function that returns `Ok(Nil)` or `Error(message)`\n"
"\n"
" ## Returns\n"
"\n"
" A `UnitNode` representing an `after_all` hook.\n"
).
-spec after_all(fun(() -> {ok, nil} | {error, binary()})) -> dream_test@types:node_(nil).
after_all(Teardown) ->
{after_all, fun(_) -> Teardown() end}.
-file("src/dream_test/unit.gleam", 745).
?DOC(
" Attach tags to a node.\n"
"\n"
" Tags propagate to descendant tests and are included in `TestResult.tags`.\n"
" Use tags to filter executed tests (e.g. in CI) with `runner.filter_tests`.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{succeed}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it, with_tags}\n"
" import gleam/io\n"
"\n"
" pub fn tests() {\n"
" describe(\"Tagged tests\", [\n"
" it(\"fast\", fn() { Ok(succeed()) })\n"
" |> with_tags([\"unit\", \"fast\"]),\n"
" it(\"slow\", fn() { Ok(succeed()) })\n"
" |> with_tags([\"integration\", \"slow\"]),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `node`: a test or group node to tag (tags do not apply to hooks)\n"
" - `tags`: tags to attach; group tags are inherited by descendant tests\n"
"\n"
" ## Returns\n"
"\n"
" The updated `UnitNode` with tags set.\n"
).
-spec with_tags(dream_test@types:node_(nil), list(binary())) -> dream_test@types:node_(nil).
with_tags(Node, Tags) ->
case Node of
{group, Name, _, Children} ->
{group, Name, Tags, Children};
{test, Name@1, _, Kind, Run, Timeout_ms, Source} ->
{test, Name@1, Tags, Kind, Run, Timeout_ms, Source};
Other ->
Other
end.