JSON output
description: "The --json output of sendra run and test: one JSON object per run with responses, assertion results, captures and a test summary."
JSON output
--json replaces the terminal output with one JSON object describing the whole
run, on stdout. Both subcommands take it:
sendra run collection.yaml --json | jq '.requests[] | select(.response.status >= 400)'
sendra test collection.yaml --json > results.json
Stdout holds the document and nothing else. The → label lines and every
error message stay on stderr, where they already were, so a redirected stdout is
a file jq can read and a terminal still shows what went wrong as it happens.
Exit codes are unchanged. --json is a different serialisation of the same
result, not part of deciding it: the table applies to both
renderings, and a run reports the same number either way.
One object per invocation, not one per request. A stream of objects would
make sendra run collection.yaml --json | jq . a stream of documents rather
than a document, and test's summary would have nowhere to live in it. The cost
is that nothing is printed until the run is over.
sendra run --json
{
"requests": [
{
"label": "Get user",
"response": {
"status": 200,
"status_text": "OK",
"elapsed_ms": 412,
"headers": [
{ "name": "content-type", "value": "application/json" }
],
"body": "{\"id\":7,\"name\":\"ada\"}"
},
"error": null,
"post_request": null,
"assertions": {
"total": 2,
"passed": 1,
"failed": 1,
"results": [
{
"kind": "status",
"expectation": "status is 200",
"passed": true,
"failure": null
},
{
"kind": "json_path",
"expectation": "`$.name` is \"ada\"",
"passed": false,
"failure": "got \"grace\""
}
]
}
}
]
}
requests— one entry per request the run attempted, in file order.label— the request'sname, orMETHOD urlwhen it has none. The same label the→line on stderr shows.responseanderror— always both present, exactly one of themnull. A request either came back or it did not;errorcarries the message and its causes joined with:, so a connection failure names both the request and the reason.body— the raw body, exactly as it arrived. The indenting described under Running requests is for a terminal; rewriting the server's bytes inside a document about them would misreport what came back.jqhasfromjsonwhen you want it parsed.headers— a list of{name, value}objects rather than one object keyed by name, because HTTP lets a header repeat (set-cookie) and a map would drop all but one of them. Wire order is preserved.assertions— always an object, with an emptyresultslist for a request that declared none.kindis one ofstatus,header,body_containsorjson_path— the keys theassertionsblock is written with.expectationandfailureare the same strings the terminal prints.post_request—nullfor a request that declared no script, which is a different thing from a script that ran and passed; otherwise{"passed": true, "failure": null}or{"passed": false, "failure": "expected 201, got 500"}. The same pair, in the same spelling, that each assertion result carries, because it is the same kind of statement about the same response. There is no matchingpre_requestkey: apre_requestscript that fails means the request was never sent, whicherroralready says, and one that succeeds has nothing to report beyond the request that went out.capture—nullfor a request that declared nocaptureblock, which is a different thing from a block that captured nothing; otherwise an object with two keys.valuesis a plain name-to-value object, so chaining a captured token into another tool is.requests[0].capture.values.auth_tokenrather than a search through a list.failuresis a list of{variable, path, failure}for the entries that produced no value, empty when they all did —failureis core's own wording, the same string the terminal shows. The values are here even though the terminal does not print them: the document already carries every response body verbatim, so they are text that is in the output twice rather than a secret this key newly exposes.
sendra test --json
The same document, plus a summary object holding the counts the terminal run
ends with:
{
"requests": [ "..." ],
"summary": {
"total": 4,
"passed": 2,
"failed": 1,
"without_assertions": 1,
"no_response": 0
}
}
Every count is present, zeroes included — the terminal leaves a zero out, and a
script reading .summary.failed should not have to know that. The four
categories are the ones described under Testing —
including the rule that a post_request failure is counted in failed — and
they still add up to total.
Two differences from the terminal output are worth stating:
summaryis absent underrun, rather than null.runhas no summary; it is not a summary that is empty.requestscarries whole responses undertest— headers and body included — where the terminal shows a status line only. That brevity is a decision about what is readable on a screen, and a program reading the output has no such problem.
When the run never starts, stdout stays empty. A missing file, a config that
does not parse, a --env naming an environment that is not there: these fail
before the first request, so there is no document to write. The error is on
stderr and the exit code is 1, as it is without the flag.
No stability promise yet. Sendra has no external consumers of this format yet; the shape above is the one to script against today, and it will grow keys before it is frozen.