# Testing

> Write test blocks with assert next to your code and run them with brine test, which runs each test in its own process, in parallel, with plain or JSON output.

Source: https://docs.brinelang.org/language/testing/

A `test "name":` block is a function with no parameters that passes when it finishes and fails when it panics. Put tests in the same file as the code they test, and run them all with `brine test`. Each test runs in its own process, in parallel with the others.

stats.br

```brine
fn mean(xs: [float]) -> float:
    return sum(x for x in xs) / float(len(xs))


fn main():
    print(mean([1.0, 2.0, 6.0]))


test "mean of three numbers":
    assert mean([1.0, 2.0, 6.0]) == 3.0


test "mean of one number":
    assert mean([4.5]) == 4.5
```

```sh
brine test stats.br
```

Output

```text
     Built tests [dev] in 0.09s
test mean of three numbers ... ok
test mean of one number ... ok


test result: ok. 2 passed; 0 failed; finished in 0.02s
```

## Writing tests

[Section titled “Writing tests”](#writing-tests)

* `test "name":` goes at the top level of any `.br` file, next to the functions it tests. The name is any string.
* The body is ordinary code. It can call any function in the file or in imported modules, use `var`s, loops and `par`, and read files.
* `assert cond` panics when `cond` is false. `assert cond, message` adds a message (any `str`, often an f-string).
* Any panic fails the test: a failed `assert`, an out-of-range index, a missing map key, an `unwrap()` of `None`, or `panic(...)`.
* Tests aren’t part of the program: `brine run` and `brine build` leave them out.

`assert` is not only for tests. In normal code it’s a runtime check that always runs, and the prover treats the condition as a known fact after it. See [contracts](/proofs/overview/).

## Failing tests

[Section titled “Failing tests”](#failing-tests)

stats.br

```brine
fn mean(xs: [float]) -> float:
    return sum(x for x in xs) / float(len(xs))


fn main():
    print(mean([1.0, 2.0, 6.0]))


test "mean of three numbers":
    assert mean([1.0, 2.0, 6.0]) == 3.0


test "mean of one number":
    assert mean([4.5]) == 4.5


test "mean of nothing":
    let empty: [float] = []
    print("about to divide by", len(empty))
    let m = mean(empty)
    assert m == 0.0, f"mean([]) was {m}"
```

brine test stats.br

```text
     Built tests [dev] in 0.09s
test mean of three numbers ... ok
test mean of one number ... ok
test mean of nothing ... FAILED


---- mean of nothing ----
about to divide by 0


panic: assertion failed: m == 0.0
  message: mean([]) was nan
  --> stats.br:17:5


test result: FAILED. 2 passed; 1 failed; finished in 0.02s
```

* What a test prints is captured. It’s shown only when the test fails, in that test’s section, followed by the panic message, the failed condition and its location.
* `brine test` exits with 0 when every test passes and 1 when any fails.
* Results are listed in the order the tests appear, whichever finishes first.

## Running tests

[Section titled “Running tests”](#running-tests)

```sh
brine test                     # every test in the project
brine test "mean"              # only tests whose name contains "mean"
brine test stats.br            # the tests in one file (and the modules it imports)
brine test stats.br "nothing"  # one file, filtered
brine test --release           # build the tests with optimizations
brine test --json              # one JSON object per test
```

* In a project, `brine test` builds `src/main.br` and every module it imports, and runs all their tests. A module’s tests are named with the module first: `geometry: a 2 by 3 rectangle`. See [modules](/language/modules/).
* The filter is a case-sensitive substring of the test name (including the `module: `part). When nothing matches, `brine test` says so and exits with 0.
* Tests run in parallel, as many at a time as you have cores. Each one is a separate process, so a panic in one test can’t affect another. Each test process runs `par` code on 2 threads.

## Tests that run the compiler

[Section titled “Tests that run the compiler”](#tests-that-run-the-compiler)

While tests run, the environment variable `BRINE` holds the path of the `brine` that is running them. A test can use it to check that a program compiles, or that it doesn’t:

compiler_test.br

```brine
# Check that a program with a known mistake is rejected with the right error.


fn check_output(source: str) -> Result[str, str]:
    let path = os.temp_dir() + "/brine-test-" + str(os.pid()) + ".br"
    write_file(path, source)?
    let cmd = [getenv("BRINE").unwrap(), "check", path, "--color", "never"]
    let p = os.spawn(cmd, {}, true, false)?
    os.wait(p)
    let (out, err) = os.output(p)
    os.remove_file(path)?
    return Ok(out + err)


test "reading and changing in one expression is rejected":
    let src = "fn main():\n    var xs = [1, 2]\n    print(xs.pop(), xs)\n"
    let out = check_output(src).unwrap()
    assert "error[E0405]" in out, out
```

brine test compiler_test.br

```text
     Built tests [dev] in 0.10s
test reading and changing in one expression is rejected ... ok


test result: ok. 1 passed; 0 failed; finished in 0.04s
```

The file name includes `os.pid()` because tests run at the same time; each test process has its own id. See [the os module](/language/os-module/) for `os.spawn`.

## JSON output

[Section titled “JSON output”](#json-output)

With `--json`, `brine test` prints one JSON object per test, in order, and nothing else:

brine test stats.br --json

```text
{"test":"mean of three numbers","ok":true,"seconds":0.022,"output":""}
{"test":"mean of one number","ok":true,"seconds":0.026,"output":""}
{"test":"mean of nothing","ok":false,"seconds":0.029,"output":"about to divide by 0\n\npanic: assertion failed: m == 0.0\n  message: mean([]) was nan\n  --> stats.br:17:5\n"}
```

| Field     | Type   | Meaning                                                                 |
| --------- | ------ | ----------------------------------------------------------------------- |
| `test`    | string | the test’s name                                                         |
| `ok`      | bool   | whether it passed                                                       |
| `seconds` | number | how long its process ran                                                |
| `output`  | string | everything it printed to stdout and stderr, including the panic message |

The exit code is the same as without `--json`. Agents and CI scripts can read the lines one at a time. See [the CLI](/tooling/cli/).

## Rules

[Section titled “Rules”](#rules)

* Tests live next to the code, in `test "name":` blocks.
* A test fails if it panics; `assert cond, message` is the usual way.
* `brine test [file] [filter]` runs every matching test, each in its own process, in parallel.
* Output is shown only for failing tests (and always in `--json`).
* `$BRINE` is the compiler running the tests.

Related: [modules](/language/modules/), [error handling](/language/error-handling/), [built-in functions](/language/builtins/), [the CLI](/tooling/cli/).
