dream_test/assertions/should

Assertion API for dream_test.

This module provides a fluent, pipe-friendly assertion API. Every assertion chain starts with should() and ends with or_fail_with().

Basic Pattern

value
|> should()
|> equal(expected)
|> or_fail_with("Helpful error message")

Available Matchers

CategoryMatchers
Equalityequal, not_equal
Booleanbe_true, be_false
Optionbe_some, be_none
Resultbe_ok, be_error
Collectionscontain, not_contain, have_length, be_empty
Comparisonbe_greater_than, be_less_than, be_at_least, be_at_most, be_between, be_in_range
Stringstart_with, end_with, contain_string
Snapshotmatch_snapshot, match_snapshot_inspect

Chaining Matchers

Matchers can be chained. Each matcher passes its unwrapped value to the next:

// Unwrap Some, then check the inner value
Some(42)
|> should()
|> be_some()
|> equal(42)
|> or_fail_with("Should be Some(42)")

// Unwrap Ok, then check the inner value
Ok("hello")
|> should()
|> be_ok()
|> equal("hello")
|> or_fail_with("Should be Ok with 'hello'")

// Unwrap Ok, then check the inner Option
Ok(Some(42))
|> should()
|> be_ok()
|> be_some()
|> be_greater_than(40)
|> or_fail_with("Should be Ok(Some(n)) where n > 40")

Explicit Failures

Sometimes you need to fail a test explicitly in a conditional branch:

case result {
  Ok(user) -> {
    user.name
    |> should()
    |> equal("Alice")
    |> or_fail_with("User should be Alice")
  }
  Error(_) -> fail_with("Should have returned a user")
}

Import Style

For best readability, import the commonly used functions unqualified:

import dream_test/assertions/should.{
  should, equal, be_ok, be_some, or_fail_with, fail_with, succeed,
}

Values

pub const be_at_least: fn(types.MatchResult(Int), Int) -> types.MatchResult(
  Int,
)

Assert that an integer is at least a minimum value (>=).

Example

user.age
|> should()
|> be_at_least(18)
|> or_fail_with("User must be at least 18")
pub const be_at_most: fn(types.MatchResult(Int), Int) -> types.MatchResult(
  Int,
)

Assert that an integer is at most a maximum value (<=).

Example

password.length
|> should()
|> be_at_most(128)
|> or_fail_with("Password must be at most 128 characters")
pub const be_between: fn(types.MatchResult(Int), Int, Int) -> types.MatchResult(
  Int,
)

Assert that an integer is between two values (exclusive).

The value must be strictly greater than min and strictly less than max.

Example

port
|> should()
|> be_between(1024, 65535)
|> or_fail_with("Port must be between 1024 and 65535")
pub const be_empty: fn(types.MatchResult(List(a))) -> types.MatchResult(
  List(a),
)

Assert that a list is empty.

Example

get_errors()
|> should()
|> be_empty()
|> or_fail_with("Should have no errors")
pub const be_error: fn(types.MatchResult(Result(a, b))) -> types.MatchResult(
  b,
)

Assert that a Result is Error and extract the error value.

If the assertion passes, the error value is passed to subsequent matchers.

Example

parse_int("not a number")
|> should()
|> be_error()
|> or_fail_with("Should fail to parse")

// With chaining:
validate(input)
|> should()
|> be_error()
|> equal(ValidationError("email required"))
|> or_fail_with("Should fail with email error")
pub const be_false: fn(types.MatchResult(Bool)) -> types.MatchResult(
  Bool,
)

Assert that a value is False.

Example

is_empty(list)
|> should()
|> be_false()
|> or_fail_with("List should not be empty")
pub const be_greater_than: fn(types.MatchResult(Int), Int) -> types.MatchResult(
  Int,
)

Assert that an integer is greater than a threshold.

Example

count_items()
|> should()
|> be_greater_than(0)
|> or_fail_with("Should have at least one item")
pub const be_greater_than_float: fn(
  types.MatchResult(Float),
  Float,
) -> types.MatchResult(Float)

Assert that a float is greater than a threshold.

Example

average
|> should()
|> be_greater_than_float(0.0)
|> or_fail_with("Average should be positive")
pub const be_in_range: fn(types.MatchResult(Int), Int, Int) -> types.MatchResult(
  Int,
)

Assert that an integer is within a range (inclusive).

The value must be >= min and <= max.

Example

score
|> should()
|> be_in_range(0, 100)
|> or_fail_with("Score must be 0-100")
pub const be_less_than: fn(types.MatchResult(Int), Int) -> types.MatchResult(
  Int,
)

Assert that an integer is less than a threshold.

Example

response_time_ms
|> should()
|> be_less_than(100)
|> or_fail_with("Response should be under 100ms")
pub const be_less_than_float: fn(types.MatchResult(Float), Float) -> types.MatchResult(
  Float,
)

Assert that a float is less than a threshold.

Example

error_rate
|> should()
|> be_less_than_float(0.01)
|> or_fail_with("Error rate should be under 1%")
pub const be_none: fn(types.MatchResult(option.Option(a))) -> types.MatchResult(
  Nil,
)

Assert that an Option is None.

Example

find_deleted_user(id)
|> should()
|> be_none()
|> or_fail_with("Deleted user should not exist")
pub const be_ok: fn(types.MatchResult(Result(a, b))) -> types.MatchResult(
  a,
)

Assert that a Result is Ok and extract its value.

If the assertion passes, the Ok value is passed to subsequent matchers. This enables chaining like be_ok() |> equal(42).

Example

parse_int("42")
|> should()
|> be_ok()
|> or_fail_with("Should parse successfully")

// With chaining:
parse_int("42")
|> should()
|> be_ok()
|> equal(42)
|> or_fail_with("Should parse to 42")
pub const be_some: fn(types.MatchResult(option.Option(a))) -> types.MatchResult(
  a,
)

Assert that an Option is Some and extract its value.

If the assertion passes, the inner value is passed to subsequent matchers. This enables chaining like be_some() |> equal(42).

Example

find_user(id)
|> should()
|> be_some()
|> or_fail_with("User should exist")

// With chaining:
find_user(id)
|> should()
|> be_some()
|> equal(expected_user)
|> or_fail_with("Should find the expected user")
pub const be_true: fn(types.MatchResult(Bool)) -> types.MatchResult(
  Bool,
)

Assert that a value is True.

Example

is_valid(input)
|> should()
|> be_true()
|> or_fail_with("Input should be valid")
pub const clear_snapshot: fn(String) -> Result(Nil, String)

Delete a snapshot file.

Use this to force regeneration of a snapshot on the next test run.

Example

let _ = clear_snapshot("./test/snapshots/old.snap")
pub const clear_snapshots_in_directory: fn(String) -> Result(
  Int,
  String,
)

Delete all snapshot files in a directory.

Deletes all files with the .snap extension in the given directory.

Example

let _ = clear_snapshots_in_directory("./test/snapshots")
pub const contain: fn(types.MatchResult(List(a)), a) -> types.MatchResult(
  List(a),
)

Assert that a list contains a specific item.

Example

[1, 2, 3]
|> should()
|> contain(2)
|> or_fail_with("List should contain 2")
pub const contain_string: fn(types.MatchResult(String), String) -> types.MatchResult(
  String,
)

Assert that a string contains a substring.

Example

log_message
|> should()
|> contain_string("error")
|> or_fail_with("Log should mention error")
pub const end_with: fn(types.MatchResult(String), String) -> types.MatchResult(
  String,
)

Assert that a string ends with a suffix.

Example

filename
|> should()
|> end_with(".gleam")
|> or_fail_with("File should be a Gleam file")
pub const equal: fn(types.MatchResult(a), a) -> types.MatchResult(
  a,
)

Assert that a value equals the expected value.

Uses Gleam’s structural equality (==).

Example

add(2, 3)
|> should()
|> equal(5)
|> or_fail_with("2 + 3 should equal 5")
pub fn fail_with(message: String) -> types.AssertionResult

Explicitly fail a test with a message.

Use this when you need to fail a test in a conditional branch where the normal assertion chain doesn’t apply.

Example

case result {
  Ok(value) -> {
    value
    |> should()
    |> equal(expected)
    |> or_fail_with("Value should match")
  }
  Error(_) -> fail_with("Should have succeeded but got an error")
}

When to Use

  • In case branches that represent unexpected states
  • When testing that something does NOT happen
  • As a placeholder for unimplemented test branches
pub const have_length: fn(types.MatchResult(List(a)), Int) -> types.MatchResult(
  List(a),
)

Assert that a list has a specific length.

Example

get_users()
|> should()
|> have_length(3)
|> or_fail_with("Should have 3 users")
pub const match_snapshot: fn(types.MatchResult(String), String) -> types.MatchResult(
  String,
)

Assert that a string matches the content of a snapshot file.

  • If snapshot doesn’t exist: creates it and passes
  • If snapshot exists and matches: passes
  • If snapshot exists but doesn’t match: fails

To update a snapshot: delete the file and re-run the test.

Example

render_html()
|> should()
|> match_snapshot("./test/snapshots/page.snap")
|> or_fail_with("HTML should match snapshot")
pub const match_snapshot_inspect: fn(types.MatchResult(a), String) -> types.MatchResult(
  a,
)

Assert that any value matches a snapshot (using string.inspect).

Serializes the value using string.inspect and compares against the stored snapshot. Useful for testing complex data structures.

Example

build_config()
|> should()
|> match_snapshot_inspect("./test/snapshots/config.snap")
|> or_fail_with("Config should match snapshot")
pub const not_contain: fn(types.MatchResult(List(a)), a) -> types.MatchResult(
  List(a),
)

Assert that a list does not contain a specific item.

Example

["a", "b", "c"]
|> should()
|> not_contain("d")
|> or_fail_with("List should not contain 'd'")
pub const not_equal: fn(types.MatchResult(a), a) -> types.MatchResult(
  a,
)

Assert that a value does not equal the unexpected value.

Example

divide(10, 3)
|> should()
|> not_equal(3)
|> or_fail_with("10/3 should not equal 3 exactly")
pub fn or_fail_with(
  result: types.MatchResult(a),
  message: String,
) -> types.AssertionResult

Complete an assertion chain and provide a failure message.

This is the terminal operation that ends every assertion chain. It converts the MatchResult into an AssertionResult that the test runner understands.

If the assertion passed, returns AssertionOk. If it failed, returns AssertionFailed with the provided message.

Example

result
|> should()
|> equal(42)
|> or_fail_with("Result should be 42")

Writing Good Messages

Good failure messages explain what should have happened:

  • ✓ “User should be authenticated after login”
  • ✓ “Cart total should include tax”
  • ✗ “wrong”
  • ✗ “failed”
pub fn should(value: a) -> types.MatchResult(a)

Start an assertion chain.

This wraps any value in a MatchResult so it can be piped into matchers. Every assertion chain should start with this function.

Example

42
|> should()
|> equal(42)
|> or_fail_with("Should be 42")
pub const start_with: fn(types.MatchResult(String), String) -> types.MatchResult(
  String,
)

Assert that a string starts with a prefix.

Example

greeting
|> should()
|> start_with("Hello")
|> or_fail_with("Greeting should start with Hello")
pub fn succeed() -> types.AssertionResult

Explicitly mark an assertion as successful.

Use this when you need to explicitly succeed in a conditional branch, as the counterpart to fail_with.

Example

case result {
  Ok(_) -> succeed()
  Error(_) -> fail_with("Should have succeeded")
}

When to Use

  • In case branches where success is the expected outcome
  • When all branches of a case must return an AssertionResult
  • To make intent explicit rather than relying on implicit success
Search Document