Getting started
On this page
Try it in 30 seconds #
Before installing anything, prove the loop against a command you already have. If you have Go, paste this — it records a real run and replays it as a test:
go run github.com/nao1215/atago@latest record --out demo.atago.yaml -- git --version
go run github.com/nao1215/atago@latest run demo.atago.yaml
.
PASSED 1 scenario: 1 passed, 0 failed, 0 errored, 0 skipped
Open demo.atago.yaml: record captured the exit code, the version line on stdout, and an empty stderr, so you have a real test to tighten rather than YAML written from scratch. Swap git --version for any command you have (go version, jq --version, ls -la). Then install atago and point it at your own tool.
Start from a real run #
You don’t write the first spec — your tool does. atago record -- <command> runs it once and generates a spec from what it observed (exit code, output, created files):
$ atago record --out mytool.atago.yaml -- mytool convert input.txt
recorded: exit 0, 2 stdout line(s), 1 file(s) created
wrote mytool.atago.yaml
$ atago run mytool.atago.yaml
.
PASSED 1 scenario: 1 passed, 0 failed, 0 errored, 0 skipped (12ms)
Interactive tools record too: atago record --pty -- <command> runs it in a real terminal, lets you drive one session by hand, and writes a pty: step that replays your keystrokes as expect/send pairs. It works on Linux, macOS, and Windows (a ConPTY); on POSIX a password prompt becomes an ${env:...} placeholder automatically, while on Windows — where a ConPTY exposes no echo state — you convert a secret send to ${env:...} by hand. A --pty session is bounded by --timeout (default 30s): if the program never exits, atago kills it, writes whatever was captured, and fails instead of hanging forever:
$ atago record --pty --out wizard.atago.yaml -- mytool init
Prefer a blank template? atago init scaffolds one. Either way, the shape is always the same: declare a command, run it, assert on what it produced.
1. Check exit code, stdout, and stderr #
version: "1"
suite:
name: example
scenarios:
- name: echo greets the world
steps:
- run:
shell: true # portable: echo is a shell builtin on Windows
command: echo "hello atago"
- assert:
exit_code: 0
stdout:
contains: atago
stderr:
empty: true
atago run accepts spec files and directories (searched recursively for *.atago.yaml; the *.atago.yml spelling is accepted too). Each scenario runs in its own temporary directory, and progress streams as a dot per scenario (. pass, F fail, E error, s skip):
$ atago run ./specs
...............................................
PASSED 47 scenarios: 47 passed, 0 failed, 0 errored, 0 skipped (1.2s)
Scenarios run concurrently by default (--parallel N, defaulting to your CPU count; set --parallel 1 to serialize). Workdirs are isolated, but the host network is shared — so if two scenarios each start a background service:, give them distinct ports, or one scenario’s requests can reach the other’s server.
When a check fails, atago prints exactly what was expected and what happened; multi-line mismatches render a colorized unified diff:
FAILED: demo / greeting matches its golden
Step:
assert stdout snapshot
Diff (-expected +actual):
--- snapshot (golden)
+++ actual
@@ -1,3 +1,3 @@
hello
-WORLD
+world
bye
Hint:
stdout did not match snapshot "snaps/greeting.txt" (update with --update-snapshots if intended)
2. Check generated files and snapshots #
fixture: writes input files into the isolated workdir; file:/dir: assertions check what the command produced, and snapshot: pins output to a committed golden file (volatile details like temp paths, UUIDs, and timestamps are normalized). A fixture’s source is one of content: (inline text), base64: (inline bytes), from: (copy an existing file), or symlink: (link to a target):
scenarios:
- name: the generator writes the expected files
steps:
- run:
command: mytool generate --out site
- assert:
file:
path: site/index.html
contains:
- "<html"
- assert:
stdout:
snapshot: snapshots/generate.txt # record/refresh with `atago snapshot update`
See files_and_fixtures, snapshot, and dir_tree for whole-tree golden manifests.
3. Drive interactive prompts and TUIs #
A pty step runs the command in a real pseudo-terminal and drives it with a declarative expect/send session — wizards, REPLs, and TTY-detection branches, no expect(1) scripting:
scenarios:
- name: the init wizard completes
steps:
- pty:
command: mytool init
session:
- expect: "Project name:"
- send: "demo\n"
- expect: "created demo/"
- assert:
exit_code: 0
Named keys (send: {key: enter}) and asserts on the RENDERED terminal screen cover full TUIs — see pty, pty_screen, and the cross-platform pty_portable. pty steps and atago record --pty run on Linux, macOS, and Windows (where they drive a ConPTY pseudo-console); only signal: stays POSIX-only.
When your CLI talks to a server #
The same YAML also drives HTTP, database, SSH, gRPC, headless-browser, and offline mock-server peers — as dependencies of the CLI under test. atago init --template <name> scaffolds each:
$ atago init --list-templates
browser drive a headless Chrome; assert page content (needs Chrome on PATH)
cli run a command; assert exit code/stdout/stderr (runs as-is)
db run SQL; assert on rows via bundled SQLite (runs as-is)
grpc call a unary gRPC method via server reflection (edit target first)
http call an HTTP API; assert status and JSON body (edit base_url first)
mock stub an HTTP API offline and assert what the client sent (needs curl on PATH)
services test against a background server: readiness, retry, teardown (runs as-is)
ssh run a command on a remote host over SSH (edit host/user first)
Next #
The cookbook has a copyable spec for most jobs and indexes the runnable example for every feature, and Use it in CI wires a suite into GitHub Actions.