expect

    A fluent assertion library for MoonBit

    testing
    assertions
    expect
    matchers
    Download zip
    Author
    Version
    0.6.0
    License
    Apache-2.0
    Last updated
    15 hours ago
    Downloads
    1K

    Dependencies

    #moonrockz/expect

    A fluent assertion library for MoonBit.

    #Install

    moon add moonrockz/expect

    Then import the package for tests in your moon.pkg:

    import {
    "moonrockz/expect",
    } for "test"

    #Usage

    docs/matchers.md lists every matcher, grouped by the type of the value under test, with an example of each.

    test "basic assertions" {
    // Equality
    @expect.expect(1 + 1).to_equal(2)
    @expect.expect(1).to_not_equal(2)

    // Booleans
    @expect.expect(true).to_be_true()
    @expect.expect(false).to_be_false()

    // Ordering (any type that implements Compare)
    @expect.expect(5).to_be_greater_than(3)
    @expect.expect(5).to_be_greater_than_or_equal(5)
    @expect.expect(3).to_be_less_than(5)
    @expect.expect(3).to_be_less_than_or_equal(3)
    @expect.expect(3).to_be_between(1, 5) // inclusive
    @expect.expect(4).to_be_between(1, 5, high_inclusive=false)

    // Signs (Int, Int16, Int64, UInt, UInt16, UInt64, Double, Float)
    @expect.expect(3).to_be_positive()
    @expect.expect(-3).to_be_negative()
    @expect.expect(0).to_be_zero()

    // Floating-point numbers (Double and Float)
    @expect.expect(0.1 + 0.2).to_be_close_to(0.3) // default tolerance 1e-9
    @expect.expect(1.0).to_be_close_to(1.05, tolerance=0.1) // absolute
    @expect.expect(1000.0).to_be_close_to(1001.0, relative=0.01) // 1%
    @expect.expect(1.0).to_be_close_to(1.0000000000000002, ulps=1)
    @expect.expect(0.0 / 0.0).to_be_nan()
    @expect.expect(1.0).to_be_finite()

    // Options
    @expect.expect(Some(42)).to_be_some()
    @expect.expect((None : Int?)).to_be_none()

    // Results
    let ok : Result[Int, String] = Ok(1)
    @expect.expect(ok).to_be_ok()
    @expect.expect((Err("boom") : Result[Int, String])).to_be_err()

    // Strings
    @expect.expect("hello world").to_contain("world")
    @expect.expect("hello world").to_start_with("hello")
    @expect.expect("hello world").to_end_with("world")
    @expect.expect("order 42").to_match("[[:digit:]]+") // regex search
    @expect.expect("42").to_match_fully("[[:digit:]]+") // whole string
    @expect.expect("Hello").to_equal_ignoring_case("hello")
    @expect.expect("a b\nc").to_equal_ignoring_whitespace("abc")
    @expect.expect(" \t").to_be_blank()
    @expect.expect("a-b-c").to_contain_times("-", 2)
    @expect.expect("one two three").to_contain_substrings_in_order(["one", "three"])

    // Arrays
    @expect.expect([1, 2, 3]).to_contain_element(2)
    @expect.expect([1, 2, 3]).to_contain_all([3, 1])
    @expect.expect([3, 1, 2]).to_equal_ignoring_order([1, 2, 3])
    @expect.expect([2, 4, 6]).to_all_satisfy(x => x % 2 == 0)
    @expect.expect([1, 2, 3]).to_contain_exactly([1, 2, 3]) // same order
    @expect.expect([1, 2, 2]).to_contain_only([2, 1]) // any order, repeats allowed
    @expect.expect([1, 2, 3]).to_contain_in_order([1, 3]) // gaps allowed
    @expect.expect([1, 2, 3]).to_start_with_elements([1, 2])
    @expect.expect([1, 2, 3]).to_end_with_elements([3])
    @expect.expect([1, 2, 3]).to_contain_none_of([4, 5])
    @expect.expect([1, 2, 3]).to_any_satisfy(x => x > 2)
    @expect.expect([1, 2, 3]).to_none_satisfy(x => x > 3)
    @expect.expect([1, 2, 3, 4]).to_have_count_satisfying(2, x => x % 2 == 0)
    @expect.expect([1, 2, 2]).to_be_sorted()
    @expect.expect(["a", "bb"]).to_be_sorted_by(s => s.length())
    @expect.expect([1, 2, 3]).to_have_no_duplicates()
    @expect.expect([1, 5]).to_satisfy_respectively([
    it => it.to_equal(1),
    it => it.to_be_greater_than(2),
    ])

    // Maps
    @expect.expect({ "a": 1 }).to_contain_key("a")
    @expect.expect({ "a": 1 }).to_contain_value(1)
    @expect.expect({ "a": 1, "b": 2 }).to_contain_entry("a", 1)
    @expect.expect({ "a": 1, "b": 2 }).to_contain_entries({ "b": 2 })

    // Length (String, Array, Map, Set, views, Deque, List, and the other core collections)
    @expect.expect("").to_be_empty()
    @expect.expect([1, 2, 3]).to_have_length(3)

    // Any value
    @expect.expect(4).to_satisfy(x => x % 2 == 0, description="is even")
    @expect.expect(2).to_be_one_of([1, 2, 3])
    @expect.expect(2).to_be_in(Set([1, 2, 3]))

    // Chars (ASCII, except to_be_whitespace)
    @expect.expect('7').to_be_digit()
    @expect.expect('a').to_be_letter()
    @expect.expect(' ').to_be_whitespace()
    @expect.expect('A').to_be_upper_case()
    @expect.expect('a').to_be_lower_case()
    }

    #Negation

    not() negates the next matcher:

    @expect.expect(3).not().to_equal(5)
    @expect.expect([1, 2]).not().to_be_empty()

    #Chaining on inner values

    unwrap_some, unwrap_ok and unwrap_err assert the variant, then return an expectation on the inner value. You cannot use them after not().

    @expect.expect(Some(42)).unwrap_some().to_be_greater_than(40)
    @expect.expect(parse("1")).unwrap_ok().to_equal(1)
    @expect.expect(parse("x")).unwrap_err().to_equal(ParseError::Invalid)

    to_be_some returns Unit, so it does not chain. MoonBit does not let a statement ignore a returned value, so a return value would break code that uses to_be_some as a statement.

    Navigation methods return an expectation on a part of the value. The label becomes the path to that part, so a failure shows where the value came from:

    // Fails with: user.address.city: expect(received).to_equal(expected) ...
    @expect.expect(user, label="user")
    .get("address", u => u.address)
    .get("city", a => a.city)
    .to_equal("Paris")

    MethodOnReturns an expectation onFails when
    get(name, f)any valuef(value)never
    element(i)Arraythe element at index ii is out of range
    first(), last()Arraythe first or last elementthe array is empty
    single()Arraythe only elementthe array does not have exactly one element
    value_at(key)Mapthe value for keythe key is missing
    fst(), snd()pairthe first or second partnever

    Navigation changes the value under test, so you cannot use it after not().

    #Matcher values

    A Matcher[T] is a matcher as a value. Run one with to, combine several, or pass one to another matcher:

    @expect.expect(age).to(@expect.all_of([
    @expect.satisfying(a => a >= 18, description=">= 18"),
    @expect.is_not(@expect.equal_to(99)),
    ]))

    @expect.expect(users).to_contain_element_matching(
    @expect.field("name", u => u.name, @expect.equal_to("Ada")),
    )

    FunctionMatches values that
    equal_to(x)equal x
    satisfying(pred, description?)satisfy pred
    field(name, f, m)give a result that matches m when you apply f
    all_of([m1, m2])match every matcher
    any_of([m1, m2])match at least one matcher
    is_not(m)do not match m
    matching(description, block)pass the method matchers in block

    matching makes every method matcher available as a matcher value:

    @expect.expect(users).to_contain_element_matching(
    @expect.field("age", u => u.age, @expect.matching("an adult", it => {
    it.to_be_greater_than_or_equal(18)
    })),
    )

    A failure names each part that does not match:

    expect(received).to(matcher) Expected: all of (> 1, < 5, even) Received: 7 Mismatch: < 5: was 7 even: was 7

    To write your own, build a Matcher with a struct literal. describe is called only for failure messages, and check returns None for a match or Some(reason) for a mismatch:

    pub fn even() -> @expect.Matcher[Int] {
    {
    describe: () => "even",
    check: x => if x % 2 == 0 { None } else { Some("was odd") },
    }
    }

    Unlike custom matcher methods, these functions can be public, so a package can share them.

    #Show the expression

    moonrockz/expect/with_source has its own expect and expect_call. A failure shows the expression you wrote instead of received:

    import {
    "moonrockz/expect",
    "moonrockz/expect/with_source",
    } for "test"

    @with_source.expect(user.age + 1).to_equal(20)

    expect(user.age + 1).to_equal(expected) Expected: 20 Received: 18

    MoonBit has no macros, so the package reads the text from your source file when an assertion fails. The compiler gives the location of the argument. It depends on moonbitlang/x/fs, so it is a separate, opt-in package.

    • The file is read only when an assertion fails, and only once per file.
    • If the file cannot be read, for example because the tests run from a different directory, the headline shows received as before.
    • Navigation shows the path from there on, as with @expect.expect.
    • Soft scopes (s.expect) still show received.

    #Snapshots

    This library has no snapshot matcher, because MoonBit's own snapshot tests already do the job, and moon test --update writes the snapshots for you:

    test "order summary" {
    // Inline snapshot of the `Show` output. `--update` fills in `content`.
    inspect(summary(order), content="3 items, $12.50")
    // Inline snapshot of the `Debug` output, for any type that derives Debug.
    @debug.debug_inspect(order.items, content="...")
    }

    test "report" (it : @test.T) {
    // File snapshot, stored in __snapshot__/report.txt.
    it.writeln(render_report(data))
    it.snapshot(filename="report.txt")
    }

    Use snapshots for large outputs that you review as a whole, and matchers for the facts that must hold. You can use both in the same test.

    #Property-based tests

    for_all checks a property for many generated values with moonbitlang/core/quickcheck. Write the property with matchers:

    @expect.for_all((xs : Array[Int]) => {
    @expect.expect(xs.rev().rev()).to_equal(xs)
    })

    On failure, for_all shows the smallest counterexample that quickcheck finds, and the matcher failure for it:

    for_all(property) failed after 3 test(s) Counterexample: 50 Shrinks: 26 successful, 37 attempted Failure: src/math_test.mbt:3:46-3:75@me/app: expect(received).to_be_less_than(expected) Expected: < 50 Received: 50

    count, max_size, max_shrinks and seed are passed to @quickcheck.check. The value type must implement @quickcheck.Arbitrary and @quickcheck.Shrink.

    #Async code

    moonrockz/expect/async_expect has eventually, which retries a block of assertions until it passes or a timeout runs out. It depends on moonbitlang/async, so it is a separate package: import it only when you need it. It supports the native, js and wasm targets.

    import {
    "moonrockz/expect",
    "moonrockz/expect/async_expect",
    } for "test"

    async test "worker drains the queue" {
    start_worker(queue)
    @async_expect.eventually(() => @expect.expect(queue.length()).to_equal(0))
    // timeout=1000 and interval=50 milliseconds by default
    }

    When the time runs out, the failure shows the number of attempts and the last failure:

    eventually(block) did not pass within 1000 ms (20 attempts) Last failure: src/worker_test.mbt:3:45-3:78@me/app: expect(received).to_equal(expected) Expected: 0 Received: 2

    #Soft assertions

    expect_all runs a block of assertions and reports every failure together, instead of stopping at the first one. Create expectations with s.expect or s.expect_call:

    @expect.expect_all(s => {
    s.expect(user.name).to_equal("Ada")
    s.expect(user.age).to_be_greater_than(18)
    s.expect(user.tags).first().to_equal("admin")
    })

    2 of 3 assertions failed (1) src/user_test.mbt:12:3-12:40@me/app: expect(received).to_equal(expected) Expected: "Ada" Received: "Bob" (2) src/user_test.mbt:13:3-13:45@me/app: expect(received).to_be_greater_than(expected) Expected: > 18 Received: 17

    • The scope carries through not(), because, navigation and all, and custom matchers built on assert_that collect their failures too.
    • A failure that leaves no value to go on with, such as unwrap_some on None, stops the block. So does any other error, for example a plain @expect.expect that fails in the block. The report says which failure stopped the block.
    • s.expect_all(inner => ...) runs a nested scope. Its failures join the outer list, and when the nested block stops, the outer block goes on.

    #Equivalence

    to_be_equivalent_to compares two values field by field, and lists each difference by path. The type does not need Eq:

    @expect.expect(saved).to_be_equivalent_to(
    draft,
    excluding=["id", "items[*].id"], // skip these paths; [*] matches any index
    ignoring_order=true, // compare arrays as multisets, at every depth
    tolerance=0.01, // numbers may differ by up to 0.01
    )

    expect(received).to_be_equivalent_to(expected) Expected: equivalent to { id: 1, owner: "Ada", balance: { cents: 1050 } } Received: { id: 1, owner: "Ada", balance: { cents: 1005 } } Differences: balance.cents: expected 1050, received 1005

    Equivalence compares what Debug shows. Fields that Debug hides, for example with Repr::omitted(), always compare as equivalent. Values that Debug shows as text, such as a custom Repr::literal, compare as text.

    #Many matchers on one value

    Matchers return Unit, so they do not chain. Use all to run several matchers on the same value:

    @expect.expect(age).all(it => {
    it.to_be_greater_than(0)
    it.to_be_less_than(150)
    })

    #Errors

    Use expect_call with an arrow function to assert that code raises, or that it returns:

    @expect.expect_call(() => parse("x")).to_raise()
    @expect.expect_call(() => parse("x")).to_raise(containing="invalid")
    @expect.expect_call(() => parse("1")).not().to_raise()

    // Check the type of the error with an `is` pattern
    @expect.expect_call(() => parse("x")).to_raise_matching(
    e => e is ParseError::Invalid(_),
    description="an Invalid error",
    )

    // Chain on the error or on the returned value
    @expect.expect_call(() => parse("x")).to_raise_error().message().to_contain("invalid")
    @expect.expect_call(() => parse("1")).to_return().to_equal(1)

    containing checks the error's to_string() output. message() gives the error's to_string() output too, but for a Failure raised by fail it leaves out the source location.

    The function can return any type. When its return type is fully generic, for example () => fail("boom"), MoonBit cannot infer the type and warns. Give the function a type, or call a function that returns Unit.

    #Json

    at navigates into a Json value with a path such as items[0].sku. Keys are separated by ., and array indices are in brackets. The path appears in the failure headline. to_contain_json checks a subset: other keys are allowed, arrays must have the same length, and numbers compare by value.

    let order : Json = { "id": 7, "items": [{ "sku": "A1", "qty": 2 }] }
    @expect.expect(order).at("items[0].sku").to_equal("A1")
    @expect.expect(order).to_contain_json({ "items": [{ "qty": 2 }] })

    When a path does not exist, the failure shows the deepest part that does:

    expect(received).at(path) Expected: a value at "items[3].sku" Received: {"id":7,"items":[{"sku":"A1","qty":2}]} Found: items is an array of length 1

    #Regular expressions

    to_match uses the regex syntax of @string.Regex from moonbitlang/core. It searches the whole string, so use ^ and $ to anchor the match. Use POSIX classes such as [[:digit:]], [[:alpha:]] and [[:space:]]: \d, \s and \w are not supported. An invalid pattern fails the assertion.

    #Labels

    Give expect a label to add context to failure messages:

    // Fails with: user id: expect(received).to_equal(expected) ...
    @expect.expect(3, label="user id").to_equal(5)

    #Reasons

    because gives the reason why an assertion must hold. A failure shows it on a Because line:

    // Fails with:
    // retries: expect(received).to_equal(expected)
    // Expected: 4
    // Received: 3
    // Because: the client retries three times
    @expect.expect(3, label="retries")
    .because("the client retries three times")
    .to_equal(4)

    The reason stays through not() and navigation, and custom matchers show it too.

    #Other collections

    Array matchers take an Array. For any other collection, use expect_elements with its iter(), and all Array matchers and navigation methods work:

    @expect.expect_elements(deque.iter()).to_contain_element(3)
    @expect.expect_elements(sorted_set.iter()).first().to_equal(1)

    to_be_empty and to_have_length work on the core collections directly: ArrayView, StringView, BytesView, Deque, List, Queue, PriorityQueue, HashMap, HashSet, SortedMap, SortedSet, and the @immut maps, sets, vectors and priority queue.

    #Custom types with a length

    to_be_empty and to_have_length work on any type that implements the HasLength trait. To use these matchers on your own type, implement the trait:

    struct Bag {
    items : Array[Int]
    } derive(Debug)

    impl @expect.HasLength for Bag with length(self) {
    self.items.length()
    }

    to_be_empty_string and to_have_length_string are deprecated. Use to_be_empty and to_have_length instead.

    #Custom matchers

    Write your own matcher as a method on @expect.Expectation in your test package, and call assert_that to check the condition. assert_that gives your matcher the same failure format as the built-in matchers: negation, labels and the location of the failed call all work.

    #callsite(autofill(loc))
    fn @expect.Expectation::to_be_even(
    self : @expect.Expectation[Int],
    loc~ : SourceLoc,
    ) -> Unit raise Error {
    self.assert_that(
    self.actual % 2 == 0,
    "to_be_even",
    expected=() => "an even number",
    received=() => @debug.to_string(self.actual),
    loc~,
    )
    }

    test "custom matcher" {
    @expect.expect(4).to_be_even()
    @expect.expect(3).not().to_be_even()
    }

    A failure shows:

    expect(received).to_be_even() Expected: an even number Received: 3

    • expected describes what the matcher wants. After not(), the message adds not in front of it.
    • received shows the value under test.
    • args names the arguments in the headline, for example args="total" gives to_have_total(total).
    • details adds lines after Received, for example details=() => [("Total", total.to_string())].

    The text is built only when the assertion fails, so a passing matcher does not pay to format values. Put #callsite(autofill(loc)) on your matcher and pass loc~ to assert_that, so that the failure points at the line that calls your matcher.

    To test the failure messages of your matchers, use failure_message or expect_failure. Both run a block that must fail, and give its message without the source location, so the result does not change when the file changes:

    test "to_be_even message" {
    inspect(
    @expect.failure_message(() => @expect.expect(3).to_be_even()),
    content=(
    #|expect(received).to_be_even()
    #|Expected: an even number
    #|Received: 3
    ),
    )
    @expect.expect_failure(() => @expect.expect(3).to_be_even())
    .to_contain("Expected: an even number")
    }

    MoonBit lets a package add methods to a type from another package only when the methods are private. So a custom matcher method is available only in the package that defines it.

    #Failure messages

    All assertions raise Failure. The message starts with the location of the failed assertion, then names the matcher and shows the values:

    src/point_test.mbt:10:3-10:58@me/app FAILED: expect(received).to_equal(expected) Expected: { x: 1, y: 3 } Received: { x: 1, y: 2 } Diff (- expected, + received): @@ -1,4 +1,4 @@ { x: 1, - y: 3, + y: 2, ? ^ }

    • The location is the matcher call, so a test with many assertions shows which one failed.
    • to_equal adds a git-style line diff when a value spans more than one line once it is pretty-printed. That includes structs, arrays and multi-line strings. The values are pretty-printed with one field or element per line, so the diff shows exactly which fields changed. Unchanged lines far from a change are left out, and each group of changes gets its own @@ hunk header. For long values, only the diff is shown.
    • When one line replaces another and most of it is the same, a ? line puts carets under the characters that changed.
    • When to_equal fails on two single-line strings, a caret points at the first difference. Long strings are cut to the text around it:

      Expected: "hello world" Received: "hello wurld" ^ first difference at index 7

    • Bytes values show as a hex dump, as hexdump -C does. When to_equal fails on two Bytes values, a Difference line shows the first differing offset.
    • When to_equal fails on two maps, the message lists the Missing, Extra and Changed keys instead of a line diff. Maps are equal in any order, so a line diff could show changes that are only a different order.
    • Values that span more than one line start on their own line. Values longer than 30 lines are shortened.
    • A negated matcher shows .not in the first line and not in the Expected line.

    Failure messages use the Debug trait from moonbitlang/core/debug to show values, so strings appear quoted and escaped. Custom types must derive Debug (and Eq where the matcher compares values):

    struct Point {
    x : Int
    y : Int
    } derive(Eq, Debug)

    #Custom formatting

    To change how a type appears in failure messages, implement Debug by hand instead of deriving it. Build the output with the Repr constructors from moonbitlang/core/debug:

    ConstructorUse it to
    Repr::record(map)show a struct with the fields you choose, in your order
    Repr::ctor(name, args)show a constructor such as Money(1050)
    Repr::literal(text)show text as it is, without quotes, such as $10.50
    Repr::omitted()hide a value, shown as ...
    Repr::opaque_(name, repr)show a wrapped value, such as <Id: 42>
    Repr(value)use the Debug output of a field

    This example shows money as dollars and hides a password:

    struct Money {
    cents : Int
    } derive(Eq)

    impl @debug.Debug for Money with fn to_repr(self) {
    let dollars = self.cents / 100
    let cents = self.cents % 100
    let padding = if cents < 10 { "0" } else { "" }
    Repr::literal("$\{dollars}.\{padding}\{cents}")
    }

    struct Account {
    owner : String
    password : String
    balance : Money
    } derive(Eq)

    impl @debug.Debug for Account with fn to_repr(self) {
    Repr::record({
    "owner": Repr(self.owner),
    "password": Repr::omitted(),
    "balance": Repr(self.balance),
    })
    }

    A failed to_equal on two accounts then shows:

    expect(received).to_equal(expected) Expected: { owner: "Ada", password: ..., balance: $10.50 } Received: { owner: "Ada", password: ..., balance: $10.05 } Diff (- expected, + received): @@ -1,5 +1,5 @@ { owner: "Ada", password: ..., - balance: $10.50, + balance: $10.05, ? ^^ }

    The line diff uses the same output, so your format also controls what the diff shows.

    Formatting does not change comparison: matchers still use Eq. If two values differ only in a field that you hide, the assertion fails but the message shows no difference. Hide a field only when it cannot be the cause of a failure, or give it a short form, such as its length, instead of ....

    #Development

    Run the tests with moon test. After you add or change a public matcher, run mise run docs:catalog to update docs/matchers.md. CI fails when the catalog is out of date.

    #License

    Apache-2.0

    FloatingPoint

    pub trait FloatingPoint {
    fn as_double(Self) -> Double
    fn ulp_distance(Self, Self) -> UInt64
    fn describe(Self) -> String
    }

    A floating-point type: Double or Float.

    HasLength

    pub(open) trait HasLength {
    fn length(Self) -> Int
    }

    A value with a length, such as a String or an Array. Implement this trait to use to_be_empty and to_have_length on your own types.
    impl HasLength for String
    impl HasLength for FixedArray[T]
    impl HasLength for Bytes
    impl HasLength for Array[T]
    impl HasLength for ArrayView[T]
    impl HasLength for Map[K, V]
    impl HasLength for Deque[T]
    impl HasLength for HashMap[K, V]
    impl HasLength for HashSet[T]
    impl HasLength for HashMap[K, V]
    impl HasLength for SortedMap[K, V]
    impl HasLength for Vector[T]
    impl HasLength for VectorMap[K, V]
    impl HasLength for List[T]
    impl HasLength for Queue[T]
    impl HasLength for Set[T]
    impl HasLength for SortedMap[K, V]

    Number

    pub(open) trait Number : Compare {
    fn zero() -> Self
    }

    A number type with a zero. Implement this trait to use to_be_positive, to_be_negative and to_be_zero on your own number type.
    impl Number for Int
    impl Number for Int16
    impl Number for Int64
    impl Number for UInt
    impl Number for UInt16
    impl Number for UInt64
    impl Number for Float
    impl Number for Double

    Expectation

    pub struct Expectation[T] {
    actual : T
    negated : Bool
    label : String
    reason : String
    scope : Scope?
    source : () -> String?
    }

    Wraps a value for fluent assertion chaining.

    Expectation::all

    #callsite(autofill(loc))
    fn[T] Expectation::all(self : Expectation[T], block : (Expectation[T]) -> Unit raise, loc~ : SourceLoc) -> Unit raise

    Run several matchers on the same value: expect(age).all(it => { it.to_be_greater_than(0); it.to_be_less_than(150) }).

    Matchers return Unit and do not chain, because MoonBit does not let a statement ignore a returned value. Use all instead. Cannot be used after not().

    Expectation::assert_that

    fn[T] Expectation::assert_that(self : Expectation[T], pass : Bool, matcher : String, expected~ : () -> String, received~ : () -> String, args? : String, details? : () -> Array[(String, String)], loc~ : SourceLoc) -> Unit raise

    Raise a failure unless pass agrees with the negation state. Use this to write your own matchers with the same failure format as the built-in ones.

    • matcher is the name in the headline, such as "to_be_even".
    • expected describes what the matcher wants. Negation adds not.
    • received shows the value under test.
    • args names the matcher arguments in the headline, such as "expected".
    • details adds lines after Expected and Received, such as ("Missing", ...).

    The text is built only when the assertion fails. Put #callsite(autofill(loc)) on your matcher and pass its loc here, so that the failure points at the call of your matcher.

    Expectation::at

    #callsite(autofill(loc))
    fn Expectation::at(self : Expectation[Json], path : String, loc~ : SourceLoc) -> Expectation[Json] raise

    Return an expectation on the value at path, such as items[0].name. Keys are separated by ., and array indices are in brackets. Fails when the path does not exist. Cannot be used after not().

    Expectation::because

    fn[T] Expectation::because(self : Expectation[T], reason : String) -> Expectation[T]

    Give the reason why the assertion must hold. A failure shows the reason on a Because line: expect(retries).because("the client retries three times").to_equal(3).

    Expectation::element

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::element(self : Expectation[Array[T]], index : Int, loc~ : SourceLoc) -> Expectation[T] raise

    Assert an Array has an element at index, and return an expectation on it. Cannot be used after not().

    Expectation::first

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::first(self : Expectation[Array[T]], loc~ : SourceLoc) -> Expectation[T] raise

    Assert an Array is not empty, and return an expectation on its first element. Cannot be used after not().

    Expectation::fst

    #callsite(autofill(loc))
    fn[A, B] Expectation::fst(self : Expectation[(A, B)], loc~ : SourceLoc) -> Expectation[A] raise

    Return an expectation on the first part of a pair. Cannot be used after not().

    Expectation::get

    #callsite(autofill(loc))
    fn[T, U] Expectation::get(self : Expectation[T], name : String, f : (T) -> U, loc~ : SourceLoc) -> Expectation[U] raise

    Apply f to the value under test and return an expectation on the result. name extends the label, so failures show the path to the value. Cannot be used after not().

    Expectation::last

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::last(self : Expectation[Array[T]], loc~ : SourceLoc) -> Expectation[T] raise

    Assert an Array is not empty, and return an expectation on its last element. Cannot be used after not().

    Expectation::message

    fn Expectation::message(self : Expectation[Error]) -> Expectation[String]

    Return an expectation on the message of an error. For a Failure raised by fail, the message does not include the source location.

    Expectation::not

    fn[T] Expectation::not(self : Expectation[T]) -> Expectation[T]

    Negate the next matcher.

    Expectation::single

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::single(self : Expectation[Array[T]], loc~ : SourceLoc) -> Expectation[T] raise

    Assert an Array has exactly one element, and return an expectation on it. Cannot be used after not().

    Expectation::snd

    #callsite(autofill(loc))
    fn[A, B] Expectation::snd(self : Expectation[(A, B)], loc~ : SourceLoc) -> Expectation[B] raise

    Return an expectation on the second part of a pair. Cannot be used after not().

    Expectation::to

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::to(self : Expectation[T], matcher : Matcher[T], loc~ : SourceLoc) -> Unit raise

    Assert the actual value matches matcher.

    Expectation::to_all_satisfy

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::to_all_satisfy(self : Expectation[Array[T]], predicate : (T) -> Bool, description? : String, loc~ : SourceLoc) -> Unit raise

    Assert every element of an Array satisfies a predicate. Negated, it asserts that at least one element does not.

    Expectation::to_any_satisfy

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::to_any_satisfy(self : Expectation[Array[T]], predicate : (T) -> Bool, description? : String, loc~ : SourceLoc) -> Unit raise

    Assert at least one element of an Array satisfies a predicate.

    Expectation::to_be_between

    #callsite(autofill(loc))
    fn[T : Compare +
    Debug
    + Eq] Expectation::to_be_between(self : Expectation[T], low : T, high : T, low_inclusive? : Bool, high_inclusive? : Bool, loc~ : SourceLoc) -> Unit raise

    Assert the actual value is between low and high. Both bounds are inclusive unless you set low_inclusive or high_inclusive to false.

    Expectation::to_be_blank

    #callsite(autofill(loc))
    fn Expectation::to_be_blank(self : Expectation[String], loc~ : SourceLoc) -> Unit raise

    Assert a String is empty or holds only whitespace.

    Expectation::to_be_close_to

    #callsite(autofill(loc))
    fn[F : FloatingPoint] Expectation::to_be_close_to(self : Expectation[F], expected : F, tolerance? : Double, relative? : Double, ulps? : Int, loc~ : SourceLoc) -> Unit raise

    Assert the actual value is close to expected, within an absolute, relative or ULP tolerance. NaN is never close to any value.

    Give at most one tolerance:

    • tolerance: the largest absolute difference. This is the default, with 1.0e-9.
    • relative: the largest difference as a fraction of the larger of the two magnitudes, for example 0.01 for 1%.
    • ulps: the largest number of representable values between the two values (units in the last place).

    Expectation::to_be_digit

    #callsite(autofill(loc))
    fn Expectation::to_be_digit(self : Expectation[Char], loc~ : SourceLoc) -> Unit raise

    Assert a Char is an ASCII digit, 0 to 9.

    Expectation::to_be_empty

    #callsite(autofill(loc))
    fn[C : HasLength +
    Debug
    ] Expectation::to_be_empty(self : Expectation[C], loc~ : SourceLoc) -> Unit raise

    Assert the actual value is empty.

    Expectation::to_be_empty_string

    #callsite(autofill(loc))
    #deprecated("Use `to_be_empty` instead")
    fn Expectation::to_be_empty_string(self : Expectation[String], loc~ : SourceLoc) -> Unit raise

    Assert a String is empty.

    Expectation::to_be_equivalent_to

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::to_be_equivalent_to(self : Expectation[T], expected : T, excluding? : Array[String], ignoring_order? : Bool, tolerance? : Double, loc~ : SourceLoc) -> Unit raise

    Assert the actual value is equivalent to expected: the two values are compared field by field, through what their Debug output shows. The type does not need Eq.

    • excluding lists paths to skip, such as id, balance.cents or items[*].id. [*] matches any index.
    • ignoring_order compares arrays as multisets, at every depth.
    • tolerance lets numbers differ by up to this amount.

    Fields that Debug hides, for example with Repr::omitted(), always compare as equivalent. Values that Debug shows as text, such as a custom Repr::literal, compare as text.

    Expectation::to_be_err

    #callsite(autofill(loc))
    fn[T :
    Debug
    , E :
    Debug
    ] Expectation::to_be_err(self : Expectation[Result[T, E]], loc~ : SourceLoc) -> Unit raise

    Assert the actual Result value is Err.

    Expectation::to_be_false

    #callsite(autofill(loc))
    fn Expectation::to_be_false(self : Expectation[Bool], loc~ : SourceLoc) -> Unit raise

    Assert the actual boolean value is false.

    Expectation::to_be_finite

    #callsite(autofill(loc))
    fn[F : FloatingPoint] Expectation::to_be_finite(self : Expectation[F], loc~ : SourceLoc) -> Unit raise

    Assert the actual value is finite: not NaN and not infinite.

    Expectation::to_be_greater_than

    #callsite(autofill(loc))
    fn[T : Compare +
    Debug
    + Eq] Expectation::to_be_greater_than(self : Expectation[T], other : T, loc~ : SourceLoc) -> Unit raise

    Assert the actual value is greater than the given value.

    Expectation::to_be_greater_than_or_equal

    #callsite(autofill(loc))
    fn[T : Compare +
    Debug
    + Eq] Expectation::to_be_greater_than_or_equal(self : Expectation[T], other : T, loc~ : SourceLoc) -> Unit raise

    Assert the actual value is greater than or equal to the given value.

    Expectation::to_be_in

    #callsite(autofill(loc))
    fn[T : Hash + Eq +
    Debug
    ] Expectation::to_be_in(self : Expectation[T], set :
    Set
    [T], loc~ : SourceLoc) -> Unit raise

    Assert the actual value is in set.

    Expectation::to_be_less_than

    #callsite(autofill(loc))
    fn[T : Compare +
    Debug
    + Eq] Expectation::to_be_less_than(self : Expectation[T], other : T, loc~ : SourceLoc) -> Unit raise

    Assert the actual value is less than the given value.

    Expectation::to_be_less_than_or_equal

    #callsite(autofill(loc))
    fn[T : Compare +
    Debug
    + Eq] Expectation::to_be_less_than_or_equal(self : Expectation[T], other : T, loc~ : SourceLoc) -> Unit raise

    Assert the actual value is less than or equal to the given value.

    Expectation::to_be_letter

    #callsite(autofill(loc))
    fn Expectation::to_be_letter(self : Expectation[Char], loc~ : SourceLoc) -> Unit raise

    Assert a Char is an ASCII letter, a to z or A to Z.

    Expectation::to_be_lower_case

    #callsite(autofill(loc))
    fn Expectation::to_be_lower_case(self : Expectation[Char], loc~ : SourceLoc) -> Unit raise

    Assert a Char is an ASCII lower-case letter, a to z.

    Expectation::to_be_nan

    #callsite(autofill(loc))
    fn[F : FloatingPoint] Expectation::to_be_nan(self : Expectation[F], loc~ : SourceLoc) -> Unit raise

    Assert the actual value is NaN.

    Expectation::to_be_negative

    #callsite(autofill(loc))
    fn[T : Number +
    Debug
    + Compare + Eq] Expectation::to_be_negative(self : Expectation[T], loc~ : SourceLoc) -> Unit raise

    Assert the actual number is less than zero.

    Expectation::to_be_none

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::to_be_none(self : Expectation[T?], loc~ : SourceLoc) -> Unit raise

    Assert the actual Option value is None.

    Expectation::to_be_ok

    #callsite(autofill(loc))
    fn[T :
    Debug
    , E :
    Debug
    ] Expectation::to_be_ok(self : Expectation[Result[T, E]], loc~ : SourceLoc) -> Unit raise

    Assert the actual Result value is Ok.

    Expectation::to_be_one_of

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_be_one_of(self : Expectation[T], values : Array[T], loc~ : SourceLoc) -> Unit raise

    Assert the actual value equals one of values.

    Expectation::to_be_positive

    #callsite(autofill(loc))
    fn[T : Number +
    Debug
    + Compare + Eq] Expectation::to_be_positive(self : Expectation[T], loc~ : SourceLoc) -> Unit raise

    Assert the actual number is greater than zero.

    Expectation::to_be_some

    #callsite(autofill(loc))
    fn[T] Expectation::to_be_some(self : Expectation[T?], loc~ : SourceLoc) -> Unit raise

    Assert the actual Option value is Some.

    Expectation::to_be_sorted

    #callsite(autofill(loc))
    fn[T : Compare +
    Debug
    + Eq] Expectation::to_be_sorted(self : Expectation[Array[T]], loc~ : SourceLoc) -> Unit raise

    Assert an Array is sorted in ascending order. Equal neighbors are allowed.

    Expectation::to_be_sorted_by

    #callsite(autofill(loc))
    fn[T :
    Debug
    , K : Compare + Eq] Expectation::to_be_sorted_by(self : Expectation[Array[T]], key : (T) -> K, loc~ : SourceLoc) -> Unit raise

    Assert an Array is sorted in ascending order of key. Equal keys are allowed.

    Expectation::to_be_true

    #callsite(autofill(loc))
    fn Expectation::to_be_true(self : Expectation[Bool], loc~ : SourceLoc) -> Unit raise

    Assert the actual boolean value is true.

    Expectation::to_be_upper_case

    #callsite(autofill(loc))
    fn Expectation::to_be_upper_case(self : Expectation[Char], loc~ : SourceLoc) -> Unit raise

    Assert a Char is an ASCII upper-case letter, A to Z.

    Expectation::to_be_whitespace

    #callsite(autofill(loc))
    fn Expectation::to_be_whitespace(self : Expectation[Char], loc~ : SourceLoc) -> Unit raise

    Assert a Char is whitespace, as Char::is_whitespace defines it. This includes Unicode whitespace, not only ASCII.

    Expectation::to_be_zero

    #callsite(autofill(loc))
    fn[T : Number +
    Debug
    + Compare + Eq] Expectation::to_be_zero(self : Expectation[T], loc~ : SourceLoc) -> Unit raise

    Assert the actual number is zero. For floating-point numbers, -0.0 is zero too.

    Expectation::to_contain

    #callsite(autofill(loc))
    fn Expectation::to_contain(self : Expectation[String], substring : String, loc~ : SourceLoc) -> Unit raise

    Assert a String contains the given substring.

    Expectation::to_contain_all

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_contain_all(self : Expectation[Array[T]], elements : Array[T], loc~ : SourceLoc) -> Unit raise

    Assert an Array contains every one of the given elements. Negated, it asserts that at least one element is missing.

    Expectation::to_contain_element

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_contain_element(self : Expectation[Array[T]], element : T, loc~ : SourceLoc) -> Unit raise

    Assert an Array contains the given element.

    Expectation::to_contain_element_matching

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::to_contain_element_matching(self : Expectation[Array[T]], matcher : Matcher[T], loc~ : SourceLoc) -> Unit raise

    Assert at least one element of an Array matches matcher. A failure shows why each element does not match.

    Expectation::to_contain_entries

    #callsite(autofill(loc))
    fn[K : Hash + Eq +
    Debug
    , V : Eq +
    Debug
    ] Expectation::to_contain_entries(self : Expectation[Map[K, V]], expected : Map[K, V], loc~ : SourceLoc) -> Unit raise

    Assert a Map contains every entry of expected. Other entries are allowed.

    Expectation::to_contain_entry

    #callsite(autofill(loc))
    fn[K : Hash + Eq +
    Debug
    , V : Eq +
    Debug
    ] Expectation::to_contain_entry(self : Expectation[Map[K, V]], key : K, value : V, loc~ : SourceLoc) -> Unit raise

    Assert a Map contains key with the given value.

    Expectation::to_contain_exactly

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_contain_exactly(self : Expectation[Array[T]], expected : Array[T], loc~ : SourceLoc) -> Unit raise

    Assert an Array has exactly the given elements, in the same order.

    Expectation::to_contain_in_order

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_contain_in_order(self : Expectation[Array[T]], expected : Array[T], loc~ : SourceLoc) -> Unit raise

    Assert an Array contains the given elements in this order, with other elements allowed between them.

    Expectation::to_contain_json

    #callsite(autofill(loc))
    fn Expectation::to_contain_json(self : Expectation[Json], expected : Json, loc~ : SourceLoc) -> Unit raise

    Assert a Json value contains expected: every key of an object in expected must exist with a value that contains the expected value, at every depth. Other keys are allowed. Arrays must have the same length, and each element must contain the expected element. Numbers compare by value.

    Expectation::to_contain_key

    #callsite(autofill(loc))
    fn[K : Hash + Eq +
    Debug
    , V :
    Debug
    ] Expectation::to_contain_key(self : Expectation[Map[K, V]], key : K, loc~ : SourceLoc) -> Unit raise

    Assert a Map contains the given key.

    Expectation::to_contain_none_of

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_contain_none_of(self : Expectation[Array[T]], values : Array[T], loc~ : SourceLoc) -> Unit raise

    Assert an Array contains none of the given values.

    Expectation::to_contain_only

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_contain_only(self : Expectation[Array[T]], expected : Array[T], loc~ : SourceLoc) -> Unit raise

    Assert every element of an Array is one of expected, and every element of expected appears at least once. Order and duplicates do not matter.

    Expectation::to_contain_substrings_in_order

    #callsite(autofill(loc))
    fn Expectation::to_contain_substrings_in_order(self : Expectation[String], parts : Array[String], loc~ : SourceLoc) -> Unit raise

    Assert a String contains the given parts in this order, with other text allowed between them.

    Expectation::to_contain_times

    #callsite(autofill(loc))
    fn Expectation::to_contain_times(self : Expectation[String], text : String, count : Int, loc~ : SourceLoc) -> Unit raise

    Assert a String contains text exactly count times. Occurrences do not overlap: "aaaa" contains "aa" twice.

    Expectation::to_contain_value

    #callsite(autofill(loc))
    fn[K :
    Debug
    , V : Eq +
    Debug
    ] Expectation::to_contain_value(self : Expectation[Map[K, V]], value : V, loc~ : SourceLoc) -> Unit raise

    Assert a Map contains the given value under any key.

    Expectation::to_end_with

    #callsite(autofill(loc))
    fn Expectation::to_end_with(self : Expectation[String], suffix : String, loc~ : SourceLoc) -> Unit raise

    Assert a String ends with the given suffix.

    Expectation::to_end_with_elements

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_end_with_elements(self : Expectation[Array[T]], suffix : Array[T], loc~ : SourceLoc) -> Unit raise

    Assert an Array ends with the given elements.

    Expectation::to_equal

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_equal(self : Expectation[T], expected : T, loc~ : SourceLoc) -> Unit raise

    Assert the actual value equals the expected value.

    Expectation::to_equal_ignoring_case

    #callsite(autofill(loc))
    fn Expectation::to_equal_ignoring_case(self : Expectation[String], expected : String, loc~ : SourceLoc) -> Unit raise

    Assert a String equals expected when case is ignored.

    Expectation::to_equal_ignoring_order

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_equal_ignoring_order(self : Expectation[Array[T]], expected : Array[T], loc~ : SourceLoc) -> Unit raise

    Assert an Array has the same elements as expected, in any order. Duplicates count: [1, 1, 2] does not match [1, 2, 2].

    Expectation::to_equal_ignoring_whitespace

    #callsite(autofill(loc))
    fn Expectation::to_equal_ignoring_whitespace(self : Expectation[String], expected : String, loc~ : SourceLoc) -> Unit raise

    Assert a String equals expected when all whitespace is removed from both. Whitespace is every character for which Char::is_whitespace is true, such as spaces, tabs and newlines.

    Expectation::to_have_count_satisfying

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::to_have_count_satisfying(self : Expectation[Array[T]], count : Int, predicate : (T) -> Bool, description? : String, loc~ : SourceLoc) -> Unit raise

    Assert exactly count elements of an Array satisfy a predicate.

    Expectation::to_have_length

    #callsite(autofill(loc))
    fn[C : HasLength +
    Debug
    ] Expectation::to_have_length(self : Expectation[C], expected : Int, loc~ : SourceLoc) -> Unit raise

    Assert the actual value has the given length.

    Expectation::to_have_length_string

    #callsite(autofill(loc))
    #deprecated("Use `to_have_length` instead")
    fn Expectation::to_have_length_string(self : Expectation[String], expected : Int, loc~ : SourceLoc) -> Unit raise

    Assert a String has the given length.

    Expectation::to_have_no_duplicates

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_have_no_duplicates(self : Expectation[Array[T]], loc~ : SourceLoc) -> Unit raise

    Assert no two elements of an Array are equal.

    Expectation::to_match

    #callsite(autofill(loc))
    fn Expectation::to_match(self : Expectation[String], pattern : String, loc~ : SourceLoc) -> Unit raise

    Assert a String matches a regular expression. The match is a search: it can start anywhere in the string. Use ^ and $ to match the whole string.

    The pattern uses the syntax of @string.Regex. \d, \s and \w are not supported; use POSIX classes such as [[:digit:]]. An invalid pattern fails the assertion.

    Expectation::to_match_fully

    #callsite(autofill(loc))
    fn Expectation::to_match_fully(self : Expectation[String], pattern : String, loc~ : SourceLoc) -> Unit raise

    Assert the whole String matches a regular expression. Unlike to_match, the match must start at the start of the string and end at its end.

    The pattern uses the syntax of @string.Regex. An invalid pattern fails the assertion.

    Expectation::to_none_satisfy

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::to_none_satisfy(self : Expectation[Array[T]], predicate : (T) -> Bool, description? : String, loc~ : SourceLoc) -> Unit raise

    Assert no element of an Array satisfies a predicate.

    Expectation::to_not_equal

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_not_equal(self : Expectation[T], other : T, loc~ : SourceLoc) -> Unit raise

    Assert the actual value does not equal the given value.

    Expectation::to_raise

    #callsite(autofill(loc))
    fn[T] Expectation::to_raise(self : Expectation[() -> T raise], containing? : String, loc~ : SourceLoc) -> Unit raise

    Assert the function raises an error. Create the expectation with expect_call.

    When containing is given, the error's to_string() must also contain it. Negated, the assertion fails only when the function raises an error that matches.

    Expectation::to_raise_error

    #callsite(autofill(loc))
    fn[T] Expectation::to_raise_error(self : Expectation[() -> T raise], loc~ : SourceLoc) -> Expectation[Error] raise

    Assert the function raises an error, and return an expectation on the error. Use message() to check the error text. Cannot be used after not().

    Expectation::to_raise_matching

    #callsite(autofill(loc))
    fn[T] Expectation::to_raise_matching(self : Expectation[() -> T raise], predicate : (Error) -> Bool, description? : String, loc~ : SourceLoc) -> Unit raise

    Assert the function raises an error that satisfies predicate. Use an is pattern to check the error type: expect_call(() => parse("x")).to_raise_matching(e => e is ParseError::Invalid(_)).

    Negated, the assertion fails only when the function raises an error that satisfies the predicate.

    Expectation::to_return

    #callsite(autofill(loc))
    fn[T] Expectation::to_return(self : Expectation[() -> T raise], loc~ : SourceLoc) -> Expectation[T] raise

    Assert the function returns without an error, and return an expectation on the returned value. Cannot be used after not().

    Expectation::to_satisfy

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::to_satisfy(self : Expectation[T], predicate : (T) -> Bool, description? : String, loc~ : SourceLoc) -> Unit raise

    Assert the actual value satisfies a predicate.

    Expectation::to_satisfy_respectively

    #callsite(autofill(loc))
    fn[T :
    Debug
    ] Expectation::to_satisfy_respectively(self : Expectation[Array[T]], checks : Array[(Expectation[T]) -> Unit raise], loc~ : SourceLoc) -> Unit raise

    Run one check per element: checks[i] runs on the element at index i. The Array must have one element for each check. A failed check reports the index in its path, such as items[1]. Cannot be used after not().

    Expectation::to_start_with

    #callsite(autofill(loc))
    fn Expectation::to_start_with(self : Expectation[String], prefix : String, loc~ : SourceLoc) -> Unit raise

    Assert a String starts with the given prefix.

    Expectation::to_start_with_elements

    #callsite(autofill(loc))
    fn[T : Eq +
    Debug
    ] Expectation::to_start_with_elements(self : Expectation[Array[T]], prefix : Array[T], loc~ : SourceLoc) -> Unit raise

    Assert an Array starts with the given elements.

    Expectation::unwrap_err

    #callsite(autofill(loc))
    fn[T :
    Debug
    , E] Expectation::unwrap_err(self : Expectation[Result[T, E]], loc~ : SourceLoc) -> Expectation[E] raise

    Assert the actual Result value is Err, and return an expectation on the error. Cannot be used after not().

    Expectation::unwrap_ok

    #callsite(autofill(loc))
    fn[T, E :
    Debug
    ] Expectation::unwrap_ok(self : Expectation[Result[T, E]], loc~ : SourceLoc) -> Expectation[T] raise

    Assert the actual Result value is Ok, and return an expectation on the inner value. Cannot be used after not().

    Expectation::unwrap_some

    #callsite(autofill(loc))
    fn[T] Expectation::unwrap_some(self : Expectation[T?], loc~ : SourceLoc) -> Expectation[T] raise

    Assert the actual Option value is Some, and return an expectation on the inner value. Cannot be used after not().

    Expectation::value_at

    #callsite(autofill(loc))
    fn[K : Hash + Eq +
    Debug
    , V :
    Debug
    ] Expectation::value_at(self : Expectation[Map[K, V]], key : K, loc~ : SourceLoc) -> Expectation[V] raise

    Assert a Map contains key, and return an expectation on its value. Cannot be used after not().

    Matcher

    pub(all) struct Matcher[T] {
    describe : () -> String
    check : (T) -> String?
    }

    A check on values of type T, with a description for failure messages.

    • describe returns what the matcher wants, such as equal to 3. It is called only when a failure message is built.
    • check returns None when the value matches, or Some(reason) when it does not, such as Some("was 2").

    Build your own matcher with a struct literal: let even : Matcher[Int] = { describe: () => "even", check: x => if x % 2 == 0 { None } else { Some("was odd") } }.

    Scope

    pub struct Scope {
    // private fields
    }

    A soft-assertion scope. Create expectations with expect and expect_call: their failures are collected, and expect_all reports them together.

    Scope::expect

    fn[T] Scope::expect(self : Scope, actual : T, label? : String) -> Expectation[T]

    Create an expectation whose failures this scope collects.

    Scope::expect_all

    fn Scope::expect_all(self : Scope, block : (Scope) -> Unit raise) -> Unit

    Run block in a nested scope. Its failures join this scope. When the nested block stops early, this block goes on.

    Scope::expect_call

    fn[T] Scope::expect_call(self : Scope, f : () -> T raise, label? : String) -> Expectation[() -> T raise]

    Create an expectation on a function whose failures this scope collects.

    all_of

    fn[T] all_of(matchers : Array[Matcher[T]]) -> Matcher[T]

    A matcher for values that match every one of matchers. A mismatch names each part that does not match.

    any_of

    fn[T] any_of(matchers : Array[Matcher[T]]) -> Matcher[T]

    A matcher for values that match at least one of matchers.

    equal_to

    fn[T : Eq +
    Debug
    ] equal_to(expected : T) -> Matcher[T]

    A matcher for values equal to expected.

    expect

    fn[T] expect(actual : T, label? : String, source? : () -> String) -> Expectation[T]

    Create an expectation on a value.

    When label is not empty, failure messages start with "<label>: ".

    source returns the text to show in the headline instead of received, such as user.age. The moonrockz/expect/with_source package fills it in from the source file. It is called only when an assertion fails.

    expect_all

    #callsite(autofill(loc))
    fn expect_all(block : (Scope) -> Unit raise, loc~ : SourceLoc) -> Unit raise

    Run a block of soft assertions. Create expectations with s.expect and s.expect_call: their failures are collected, and when the block ends, one failure lists all of them.

    A failure that leaves no value to go on with, such as unwrap_some on None, stops the block. So does any other error, such as a failure of a plain @expect.expect in the block.

    expect_call

    fn[T] expect_call(f : () -> T raise, label? : String, source? : () -> String) -> Expectation[() -> T raise]

    Create an expectation on a function, for use with to_raise, to_return, to_raise_error and to_raise_matching.

    Use an arrow function so that MoonBit infers the error type: expect_call(() => parse("bad")).to_raise().

    expect_elements

    fn[T] expect_elements(items : Iter[T], label? : String) -> Expectation[Array[T]]

    Create an expectation on the elements of any collection, as an Array: expect_elements(deque.iter()).to_contain_element(3). All Array matchers and navigation methods then work.

    expect_failure

    #callsite(autofill(loc))
    fn expect_failure(f : () -> Unit raise, label? : String, loc~ : SourceLoc) -> Expectation[String] raise

    An expectation on the failure message of f, without color and without the source location: expect_failure(() => expect(3).to_be_even()).to_contain("an even number").

    Fails when f does not fail.

    failure_message

    #callsite(autofill(loc))
    fn failure_message(f : () -> Unit raise, loc~ : SourceLoc) -> String raise

    The failure message of f, without color and without the source location. Use it to test the failure messages of custom matchers: inspect(failure_message(() => expect(3).to_be_even()), content=...).

    Fails when f does not fail.

    field

    fn[T, U] field(name : String, f : (T) -> U, matcher : Matcher[U]) -> Matcher[T]

    A matcher that applies f to the value and checks the result with matcher: field("name", u => u.name, equal_to("Ada")).

    for_all

    #callsite(autofill(loc))
    fn[A :
    Arbitrary
    +
    Shrink
    +
    Debug
    ] for_all(property : (A) -> Unit raise, count? : UInt, max_size? : UInt, max_shrinks? : UInt, seed? : UInt64, loc~ : SourceLoc) -> Unit raise

    Check that property holds for generated values of type A. Write the property with matchers: for_all((x : Int) => expect(x.abs()).to_be_greater_than_or_equal(0)).

    On failure, the message shows the smallest counterexample that @quickcheck finds and the matcher failure for it. count, max_size, max_shrinks and seed are passed to @quickcheck.check.

    is_not

    fn[T :
    Debug
    ] is_not(matcher : Matcher[T]) -> Matcher[T]

    A matcher for values that do not match matcher.

    matching

    fn[T] matching(description : String, block : (Expectation[T]) -> Unit raise) -> Matcher[T]

    A matcher that runs method matchers on the value: matching("an adult", it => it.to_be_greater_than_or_equal(18)).

    Every method matcher works this way, and so do custom matchers. The mismatch comes from the failure of the first method matcher that fails.

    satisfying

    fn[T :
    Debug
    ] satisfying(predicate : (T) -> Bool, description? : String) -> Matcher[T]

    A matcher for values that satisfy predicate.