Skip to content

API testing

Two steps in the Integration category let a test talk to the application’s API directly, instead of going through its screens:

Step What it does
HTTP request (http.request) Sends one request and saves the response in a variable
Poll API (http.poll) Repeats a request until a condition on the response is true

They run inside the test’s browser context, so they share the UI session’s cookies. A request made after signing in on screen is authenticated as that user.

Typical uses:

  • Set up test data fast: create the vendor with one API call, then test the screen.
  • Tear down what the test created, in cleanup.
  • Verify that what the screen claims was really stored.
  • Wait for asynchronous work (a job, an approval) without a fixed Wait.
Parameter Required Meaning
url yes Absolute URL, a template: {{apiUrl}}/orders/{{order.body.id}}
method GET (default), POST, PUT, PATCH, DELETE, HEAD
headers Name → template, e.g. Authorization = Bearer {{auth.body.token}}
body Text sent as-is, a template. If it starts with { or [ and you set no Content-Type, it is sent as application/json.
expectStatus Accepted status codes: 201, 2xx, 200,204, 4xx, or any. Empty means any 2xx. Anything else fails the step.
saveAs Variable that receives the response
sensitive Masks every string in the response body in reports, logs and saved variables. Use it on login and token calls.

A saved response looks like this:

{
"status": 201,
"ok": true,
"headers": { "content-type": "application/json" },
"body": { "id": "V-1001", "name": "Acme", "status": "Active" },
"durationMs": 4
}

JSON bodies are parsed, so later steps can use fields:

  • in a template: {{vendor.body.id}}, in a URL, header, body or value to type,
  • in a check: an Expression assertion vendor.body.status == 'Active',
  • lists by index: orders.body.0.id, and COUNT(orders.body) > 0.

The response is saved even when the status check fails, so cleanup can still use it.

All the HTTP request parameters, plus:

Parameter Required Meaning
until yes Expression checked after each response, which is available as response, e.g. response.body.state == 'done'
intervalMs Time between requests (default 1000, max 60000)
maxAttempts Most requests to send (max 1000). The step’s Timeout also limits the whole poll.

A poll that never meets its condition fails. It cannot run forever.

1. HTTP request POST {{apiUrl}}/auth/token body {"user":"{{secret.API_USER}}",…} saveAs auth sensitive
2. Set variable vendorName = CONCAT('Vendor ', NOW())
3. HTTP request POST {{apiUrl}}/vendors header Authorization: Bearer {{auth.body.token}}
body {"name":"{{vendorName}}"} expectStatus 201 saveAs vendor
4. Open {{baseUrl}}/vendors
5. Exists text "Active" scoped to row with key {{vendor.body.id}}
  • Give records unique names so runs that share an environment do not collide.
  • To find the record on screen, scope the target to its grid row by key. Scope keys are templates; strategy values are fixed text.

Put deletes in the test’s cleanup list, which runs whether the test passed or failed. Give each cleanup step On error: Continue and accept 404:

"cleanup": [
{ "id": "delete-vendor", "type": "http.request", "onError": "continue",
"params": { "method": "DELETE", "url": "{{apiUrl}}/vendors/{{vendor.body.id}}",
"headers": { "Authorization": "Bearer {{auth.body.token}}" },
"expectStatus": "204,404" } }
]

Delete in reverse order of creation (children before parents).

Cleanup steps cannot yet be added in the editor. Add them in the test JSON. See Editor limitations.

  1. UI steps create the record. An Extract step reads its id from the screen.
  2. HTTP request GET {{apiUrl}}/vendors/{{createdId}}, saveAs stored.
  3. One Expression assertion per field, each marked Soft assertion, so one run reports every mismatch.
HTTP request POST {{apiUrl}}/orders/{{order.body.id}}/approve expectStatus 202 saveAs job
Poll API GET {{apiUrl}}/jobs/{{job.body.id}}
until response.body.state == 'done'
intervalMs 500 maxAttempts 40

examples/api-demo/ has four complete tests and a small demo API:

Terminal window
npm run demo:api
Terminal window
npm run seed:api-examples -- <projectId> <ownerUserId> --run

See examples/api-demo/README.md and Data, backup and seeding.