Skip to content

API testing

tapstep is not a Postman replacement — it makes the API a first-class citizen inside a flow. Use request: steps for setup, teardown and backend checks, and chain them with UI steps in the same file: what you create through the API you verify on screen, sharing the same ${variables}.

A flow with device: none never resolves or boots a device — it runs anywhere, instantly:

device: none
env: { API: "https://api.example.dev" }
commands:
- request:
method: POST
url: ${API}/staff
auth: { bearer: "${STAFF_TOKEN}" }
json: { email: "qa@test.dev", role: staff }
extract: { staffId: $.id } # JSONPath → ${staffId}
assert:
status: 201 # a code, or a class: "2xx"
json: { $.role: staff }
- request:
url: ${API}/staff/${staffId}
retryUntil: { status: "2xx" } # poll every second until it passes
timeout: 15000

Under device: none only request, scripts, value asserts (assertTrue, assertFile) and control flow are allowed — a UI command is a validation error. In the desktop app such flows run without the device picker; in the CLI no device is probed or auto-booted.

In a normal (device) flow, request: steps mix freely with UI steps:

appId: https://staging.example.dev
commands:
- request: # arrange: create through the API
method: POST
url: ${API}/staff
auth: { bearer: "${STAFF_TOKEN}" }
json: { email: "qa@test.dev" }
extract: { staffId: $.id }
- openLink: "${APP}/staff/${staffId}" # act + assert: verify in the UI
- assertVisible: "qa@test.dev"
Field Meaning
method, url GET by default; - request: <url> is a GET shorthand.
headers, query String maps, ${}-substituted.
json / body Structured JSON body or a raw string (one or the other).
auth { bearer: ${TOKEN} } or { basic: { user, pass } }.
extract var: $.json.path — response values into ${var} for later steps.
assert status (code or "2xx"), json equalities or { contains: "…" }, headers, schema (a JSON Schema file next to the flow, or inline).
retryUntil Same shape as assert, but polls until it passes or timeout expires — wait for the backend to reach a state.
timeout Per-request ms (default 30000); the polling deadline with retryUntil.

After every request ${response.status} and ${response.body} are available to the following steps. On a failed assertion the report carries the method, URL, status and a slice of the response body — in JUnit, Allure and HTML output alike.

Deliberately out of scope: load testing, mocks/stubs, GUI collections. For end-to-end work the common Postman moves — call, extract, assert, poll — are covered right in the flow.