Calling requests and checks
expect — check the response
Section titled “expect — check the response”CreateUser: POST /users { body { name: "Viktor" }
expect { status == 201 body.name == "Viktor" body.id matches /^u_\d+$/ headers.content-type.contains("json") duration < 500 body matches { id: string, name: string, tags?: [string] } }}One check per line; each must be true. All checks run, even after a failure, and a failed one shows why:
✗ api/users/create.outry CreateUser POST http://localhost:8080/users 201 Created 9ms 211B ✓ status == 201 ✗ body.name == "Viktor" — body.name is "viktor" ✗ body matches { id: string, name: string, tags?: [string] } — body.id: expected string, got 42Comparisons don’t coerce types ("1" != 1), a missing field is null instead of an error, and
matches takes a regular expression, an inline shape, a named shape
or a JSON Schema file. All operators and methods: Expressions.
Calling another request
Section titled “Calling another request”A request is a function: call it by name, and the result is its response.
Login: POST /auth/login { params { email: "dev@example.com" password }
body { email, password } expect { status == 200 }}Me: GET /me { headers { Authorization: "Bearer ${Login().body.token}" } expect { status == 200 }}outry run api/users/me.outry # logs in first, by itself✓ api/users/me.outry Me GET http://localhost:8080/me 200 OK 8ms 96B ↳ Login 200 31ms ✓ status == 200-
No run order to maintain.
Meworks alone — it callsLoginwhen it needs a token. -
Sent once per run. Ten requests calling
Login()log in once; the rest reuse the response (↳ Login cached).fresh Login()always sends a new one.cache: 30monLoginkeeps its response between runs. -
Arguments fill the callee’s
paramsand{path}parameters:GetUser(id: 7),Login(email: "admin@example.com"). -
Failures propagate. If
Loginfails itsexpect,Mefails too, with the chain:✗ api/users/me.outry MeMe → Login: status == 200 — status is 401 -
Cookies from
Set-Cookieare kept for the whole run, like in a browser.
Request names are unique in the project; when two folders have a Create, call users.Create().
outry check reports unknown names, wrong arguments and call cycles.
flow — a scenario
Section titled “flow — a scenario”// Checkoutflow Checkout { user = CreateUser(name: "Bob") order = CreateOrder(customer: user.body.id) Pay(order: order.body.id)
expect { GetOrder(id: order.body.id).body.status == "paid" }}Steps run top to bottom and the flow stops at the first failure. In the app the flow shows a trace of
every request it called. Run it by name: outry run Checkout.
save — keep a value
Section titled “save — keep a value”save order_id = body.idA saved value is a variable for later requests in the run and is remembered between runs, per
project and environment — so after one outry run CreateOrder, GetOrder alone still knows order_id.
Values are stored outside the repository; see Saved values.
Prefer calls for things like tokens: they don’t depend on what ran before.
Exit codes
Section titled “Exit codes”outry run exits with:
0— every request was sent and every check and save succeeded;1— at least one request failed (a check, a save, a network error, a missing variable);2— Outry could not start: invalid arguments, unknown environment, brokenenv.toml.
Use --fail-fast to stop at the first failed request.