Skip to content

API testing

Requests live in collections inside the same project as your browser tests, so they can share environments, suites and run history.

Capture requests from a session

The fastest way to get API coverage of a flow is to record the flow. A browser opens, every HTTP call your application makes is captured, and you choose which to keep.

qaclan api record --url https://your-app.test

Static assets and analytics beacons are usually noise; the calls that carry your data are the ones worth keeping. What you save becomes a named collection.

Import what you already have

qaclan api import <file_or_url> --format auto --collection <name>

Format is detected from the extension and content when --format auto is used, which is the default. Accepted values:

FormatSource
harA HAR capture exported from your browser's network tab
openapiAn OpenAPI or Swagger document — a local file or a URL, YAML or JSON
postmanA Postman collection export
brunoBruno .bru files

An OpenAPI import groups requests into collections by tag, so a well-tagged document arrives already organised. In the local UI you can also paste one or more curl commands and preview the requests before saving them.

Collections, folders and variables

A collection holds requests and folders. Collection-level variables and environment variables both resolve as {{var}} anywhere in a URL, header, query parameter or body — see Environments and secrets.

qaclan api list
qaclan api list --collection Orders

Authentication

Auth is configured on the request or inherited from the collection, and applied when the request runs rather than pasted into headers by hand. Supported types:

  • Bearer — a token sent as Authorization: Bearer …
  • Basic — username and password
  • API key — a named key in a header or query parameter
  • OAuth2

Assertions without code

An assertion is a type, an operator and an expected value. Add them in the request editor's Assertions tab; results appear per request in the run.

TypeOperatorsChecks
statuseq ne lt gtHTTP status code, compared numerically
json_patheq ne lt gt contains exists not_exists matchesThe value at a JSONPath expression in the JSON response body
headereq ne contains exists not_exists matchesA response header, matched case-insensitively by name
response_timelt gt eqRequest duration in milliseconds
body_textcontains eq matchesThe raw response body as text

matches is a real regular expression search — case-sensitive, not anchored. For a JSONPath expression that matches several nodes, a match mode decides whether the first, any or all matches must satisfy the operator.

Pre- and post-request scripts

When a check needs real logic, attach a script. Pre-scripts run before the call and can stage headers or parameters; post-scripts run after the response and are the main way a value moves into the next step of a suite. Both are available in JavaScript and Python.

post-script · javascript

const body = qc.response.json();
qc.set("order_id", body.id);
qc.expect(body.status === "confirmed", "order not confirmed");

Commonly used bindings:

BindingPurpose
qc.set(key, value)Write to shared state, so later requests can use it as a variable
qc.setHeader(key, value) / qc.set_header(...)Add or overwrite a request header before the call is sent
qc.setParam(key, value) / qc.set_param(...)Add or overwrite a query parameter before the call is sent
qc.expect(condition, message)Record a pass or fail, shown next to the no-code assertion results
qc.test(name, fn)Run a function and record one named assertion from its outcome

JavaScript uses camelCase (setHeader) and Python uses snake_case (set_header). The agent repository carries the complete syntax reference, including every binding and its edge cases.

GraphQL

A request body can be GraphQL, with a dedicated editor giving syntax highlighting, bracket matching and grammar-level completion, plus a formatter for minified documents.

No schema introspection

The editor does not load your schema, so there is no field- or type-aware autocomplete. It understands GraphQL grammar, not your particular API.

Running requests

qaclan api run <name_or_id>
qaclan api run <name_or_id> --collection <name> --env staging

Run one request while you are building it, or a whole collection against an environment. Results, including per-assertion outcomes, are recorded in run history.

Documentation from real traffic

Every request that runs updates an API reference: the method, the path with variables inferred (a call to /posts/1 becomes /posts/{post_id}), and the response shapes observed. It is visible under the API Docs tab in the local UI, and on the server if you are syncing.

Export it when someone outside the team needs a spec:

  • OpenAPI YAML — from the API Docs view
  • Postman JSON or Bruno files— from a collection's menu
qaclan api export <collection> --output ./exported

The CLI export writes Bruno .bru files. Postman and OpenAPI export are available from the local UI.

Mixing API and browser steps

The reason all of this lives in one project is that a suite can interleave API requests with recorded browser scripts and pass state between them. That is covered in Suites, runs and reports.