shell output tester
Go to file
Chris Wanstrath 1c7c0da4b7 Add Rust implementation of the shout test runner
Rewrites the shout CLI in Rust for better performance, with parallel
test execution via rayon and the same .shout file format semantics.
2026-04-02 13:28:48 -07:00
shout-rs Add Rust implementation of the shout test runner 2026-04-02 13:28:48 -07:00
src Stream command results as dots in real time 2026-03-15 20:57:28 -07:00
test Add @teardown directive support 2026-03-12 20:48:51 -07:00
vim Remove shoutEnvValue from contains list 2026-03-14 13:16:17 -07:00
web Add -t/--filter flag for file path filtering 2026-03-15 16:14:47 -07:00
.gitignore shout it out 2026-03-09 21:13:41 -07:00
.npmrc shout it out 2026-03-09 21:13:41 -07:00
bun.lock Refactor: extract shared utils, clean up deps 2026-03-10 15:12:45 -07:00
CLAUDE.md Add -t/--filter flag for file path filtering 2026-03-15 16:14:47 -07:00
package.json 0.0.18 2026-03-15 20:58:00 -07:00
README.md Add -t/--filter flag for file path filtering 2026-03-15 16:14:47 -07:00
STDIN.md docs: add stdin/heredoc design notes 2026-03-15 13:57:16 -07:00
tsconfig.json Refactor: extract shared utils, clean up deps 2026-03-10 15:12:45 -07:00

shout

shout is kinda like really basic integration testing for your cli.

Write .shout files that look like shell sessions and run shout to test 'em.

Install

bun install -g @because/shout

Features

Everything you could ever ask for:

$ echo hello
hello

$ brew --version
Homebrew 5...

$ ls missing
ls: missing: No such file or directory
[1]

... matches anything — a whole line inline, or any number of lines on its own.

[1] after the expected output matches the exit code.

$# is a comment line — not executed, no output expected:

$# start the server
$ my-server &
$# now test it
$ curl localhost:8080
OK

Usage

$ shout test
...............
15 passed in 23ms

shout test runs code in a temp directory. -k/--keep keeps it around.

--update will modify .shout files to match reality, without running any tests.

Each line in a .shout file is run sequentially, unless --parallel is passed.

Directives

Directives go at the top of a .shout file, before any commands.

@env

Set environment variables for the test:

@env GREETING=hello
@env TARGET=world

$ echo "$GREETING $TARGET"
hello world

@setup

Prepend commands (and @env directives) from another .shout file:

@setup setup-shared.shout

Setup commands run first and their failures abort the test. Setup files cannot themselves contain @setup — no nesting. If both the setup file and the user file define the same @env, the user file wins.

@teardown

Run a cleanup command after all test commands, regardless of pass/fail:

@teardown rm -f "$SHOUT_PROJECT_DIR/data/test.db"

$ create-db && run-tests
...

Teardown failures produce warnings but don't affect test results. You can also put @teardown in setup files:

# setup.shout
export DB_URL=sqlite:data/test.db
@teardown rm -f "$SHOUT_PROJECT_DIR/data/test.db"
Usage: shout test [options] [files...]

Run .shout test files

Arguments:
  files            Files or directories to test

Options:
  -u, --update     Rewrite expected output in-place with actual output
  -k, --keep       Keep temp directories after run
  --clean-env      Start with empty environment
  --path <path>    Prepend <path> to PATH (repeatable)
  --timeout <dur>  Per-command timeout (default: "10s")
  -t, --filter     Only run files matching <pattern> (substring match)
  -v, --verbose    Print each command as it runs
  --port-from <n>  Auto-assign $PORT starting from n (default: "5400")
  --parallel       Run files in parallel
  -h, --help       display help for command

Environment Variables

Set automatically

Variable Value
HOME Path to the temp directory created for the test
SHOUT_DIR Same as HOME — the temp directory for the test
SHOUT_SOURCE_DIR Directory containing the .shout file being run
SHOUT_PROJECT_DIR The cwd where shout was invoked
PORT Auto-assigned starting from 5400 (or the value of --port-from), increments per file. Not set if PORT is already defined via @env or @setup.

Inherited

By default, the test shell inherits all environment variables from the parent process. Use --clean-env to start with an empty environment instead.

Modified

PATH is prepended with any directories passed via --path <path>.

User-defined

Use @env KEY=VALUE directives to set arbitrary variables. See Directives.

Print an example .shout file:

$ shout example

Editor Support

Neovim

The vim/ directory contains a Neovim plugin with syntax highlighting and commands (:ShoutRun, :ShoutUpdate, :ShoutRunAll).

lazy.nvim

{
  dir = "~/path/to/shout/vim",
  ft = "shout",
}

Manual

mkdir -p ~/.local/share/nvim/site/pack/shout/start
ln -s /path/to/shout/vim ~/.local/share/nvim/site/pack/shout/start/shout