· │ value: "haystack"
╰────
```
In general for base `assert` command it is encouraged to always provide the additional message to show what went wrong. If you cannot use any built-in assert command, you can create a custom one with passing the label for [`error make`](/commands/docs/error_make.md) for the `assert` command:
```nu
def "assert even" [number: int] {
assert ($number mod 2 == 0) --error-label {
text: $"($number) is not an even number",
span: (metadata $number).span,
}
}
```
Then you'll have your detailed custom error message:
```nu
let $a = 13
assert even $a
```
```
Error:
× Assertion failed.
╭─[entry #37:1:1]
1 │ assert even $a
· ─┬
· ╰── 13 is not an even number
╰────
```
## Running the Tests
Now that we are able to write tests by calling commands from `std assert`, it would be great to be able to run them and see our tests fail when there is an issue and pass when everything is correct :)
### Nupm Package
In this first case, we will assume that the code you are trying to test is part of a [Nupm] package.
In that case, it is as easy as following the following steps
- create a `tests/` directory next to the `nupm.nuon` package file of your package
- make the `tests/` directory a valid module by adding a `mod.nu` file into it
- write commands inside `tests/`
- call `nupm test`
The convention is that any command fully exported from the `tests` module will be run as a test, e.g.
- `export def some-test` in `tests/mod.nu` will run
- `def just-an-internal-cmd` in `tests/mod.nu` will NOT run
- `export def another-test` in `tests/spam.nu` will run if and only if there is something like `export use spam.nu *` in `tests/mod.nu`
### Standalone Tests
If your Nushell script or module is not part of a [Nupm] package, the simplest way is to write tests in standalone scripts and then call them, either from a `Makefile` or in a CI:
Let's say we have a simple `math.nu` module which contains a simple Fibonacci command:
```nu
# `fib n` is the n-th Fibonacci number
export def fib [n: int] [ nothing -> int ] {
if $n == 0 {
return 0
} else if $n == 1 {
return 1
}
(fib ($n - 1)) + (fib ($n - 2))
}
```
then a test script called `tests.nu` could look like
```nu
use math.nu fib
use std/assert
for t in [
[input, expected];
[0, 0],
[1, 1],
[2, 1],
[3, 2],
[4, 3],
[5, 5],
[6, 8],
[7, 13],
] {
assert equal (fib $t.input) $t.expected
}
```
and be invoked as `nu tests.nu`
### Basic Test Framework
It is also possible to define tests in Nushell as functions with descriptive names and discover
them dynamically without requiring a [Nupm] package. The following uses `scope commands` and a
second instance of Nushell to run the generated list of tests.
```nu
use std/assert
source fib.nu
def main [] {
print "Running tests..."
let test_commands = (
scope commands
| where ($it.type == "custom")
and ($it.name | str starts-with "test ")
and not ($it.description | str starts-with "ignore")
| get name
| each { |test| [$"print 'Running test: ($test)'", $test] } | flatten
| str join "; "
)
nu --commands $"source ($env.CURRENT_FILE); ($test_commands)"
print "Tests completed successfully"
}
def "test fib" [] {
for t in [
[input, expected];
[0, 0],
[1, 1],
[2, 1],
[3, 2],
[4, 3],
[5, 5],
[6, 8],
[7, 13]
] {
assert equal (fib $t.input) $t.expected
}
}
# ignore
def "test show-ignored-test" [] {
print "This test will not be executed"
}
```
This is a simple example but could be extended to include many of the things you might expect from
a testing framework, including setup and tear down functions and test discovery across files.