Test your API
Make tests from traffic
Since 0.4.0 Requires Cloak Proxy 0.4.0 or later.
Select captured requests, or a whole app or host, and choose Make Tests — Cloak Proxy builds an API-client collection that runs as a test suite, with IDs and tokens chained between requests and tests inferred from the capture (status, content type, JSON schema, values that never changed, response time budgets). Run it in the collection runner, from MCP, or in CI with cloakctl.
Writing API tests by hand means describing, request by request, what correct looks like. The traffic you’ve already captured shows it: the status each call returned, the shape of every response, the values that never change, how long each one took. Make Tests turns that into a test suite — an API-client collection whose requests run in order, pass the IDs and tokens they need from one response to the next, and check each response against what was captured.
Each captured request becomes a step with tests inferred from the capture. The order ID the app got back is read from the fresh response and used by the later steps.
- Scope, options, a trial run, and Add to API Client
- A value later steps use
- How many tests, and of which kinds
- The step's tests
- The chained value
Make a suite
Section titled “Make a suite”-
Capture the journey you want to test: use the app, or run your end-to-end tests through Cloak Proxy.
-
Select the requests in the traffic list, right-click and choose Make Tests from the requests. Or right-click an app or a host in the sidebar and choose Make Tests from it, or choose Tools › Make Tests…
-
Check the steps and their tests. Change the Scope or the Options and the suite is made again straight away.
-
Click Try It to run the suite once against the real servers and see which tests pass, without saving anything.
-
Click Add to API Client (⌘ ↩). The collection is added to the API client and its collection runner opens.
Which requests become steps
Section titled “Which requests become steps”Requests keep the order they were captured in. These are left out, and listed under captured requests left out with the reason:
- encrypted tunnels (turn on SSL Proxying to see them), requests still in progress and requests that got no response;
- images, scripts, styles, fonts and media, and CORS preflight
OPTIONSrequests; - requests that got a server error (5xx) when captured, unless Include server errors is on;
- requests to analytics, advertising and other SDK companies the Privacy Map recognises, unless Include analytics and other SDKs is on;
- requests whose body was cut short when captured.
One request per endpoint keeps only the first call to each endpoint, for a shorter suite.
What’s tested
Section titled “What’s tested”Tests are inferred for each step from every captured response of its endpoint in the session, not only the selected one — so recording the journey twice gives better tests than recording it once.
| Test | Example | How it’s inferred |
|---|---|---|
| Status | Status is 201 |
The status the request got when captured |
| Content type | Content-Type is application/json |
The media type of the response, when every captured response of the endpoint had the same one |
| JSON schema | Body has the shape of 2 captured responses |
A JSON Schema of every captured response of the endpoint with the same status: each field’s type, null where a field was ever null, fields present in every response marked required, array items. Extra fields are allowed, and whole numbers are checked as numbers, so a price of 89 then 89.5 passes |
| Stable values | $.order.currency is "GBP" |
Values that were the same in every captured response (at least two): flags, labels, limits. IDs, tokens, timestamps, dates, request IDs and long text are left out, as they change between runs. Up to six per step, shallow ones first |
| Response time | Responds within 450 ms |
The slowest captured time for the endpoint, times 3 (or 1.5, 2 or 5 in Options), at least 200 ms, rounded up to 50 ms |
| Chained value | orderId is in the response |
A value a later step uses was found in this response |
With Also values seen once on, flags and short labels (true, "member") from a single response are asserted too. Turn any kind of test off in Options.
Chained values and credentials
Section titled “Chained values and credentials”A captured journey can’t be replayed as it is: the order created at step 9 has a new ID each time, and the token from the login has expired. As in replayable flows, every value a later request reused from an earlier response — an order ID, an access token, a cursor — is read from the fresh response while the suite runs, with pm.collectionVariables.set, and the later requests use {{orderId}} where the capture had the value: in the path, the query, a header or the body. Cookies an earlier response set come from the run’s cookie jar. The variables start with the captured values, so you can send one request on its own.
Authorization, Cookie and API-key headers that aren’t chained move into collection variables ({{authorization}}, {{cookie}}, {{apiKey}}), marked secret, so you update an expired token in one place. Turn off Keep the captured credentials to leave them empty, for a suite you’ll share.
The steps and the script
Section titled “The steps and the script”Each row shows the step’s number, method and path, the values it uses ({{orderId}}) and provides (→ orderId), how many tests it has with an icon per kind (hover for the counts), and its captured status and time — or, after Try It, how many of its tests passed or failed. Right-click a step to show the captured request in the traffic list.
Select a step for its tests, its chained values and its test script: the request’s post-response script in the API client, plain Postman-style JavaScript you can edit after adding the collection.
pm.test("Status is 201", function () { pm.response.to.have.status(201);});
pm.test("$.order.currency is \"GBP\"", function () { pm.expect(pm.response.json().order.currency).to.eql("GBP");});
pm.test("Responds within 750 ms", function () { pm.expect(pm.response.responseTime).to.be.below(750);});
// Values later requests use, read from this response.pm.collectionVariables.set("orderId", pm.response.json().order.id);Each request’s documentation says which captured request it came from, what was inferred and from how many responses, and which values it uses and provides. The collection’s documentation lists the chained values, the credential variables and what was left out.
Run the suite
Section titled “Run the suite”- In the app: the collection runner opens when you add the suite. Its results show each test, and failures say what was expected and what came back. See Collection runner.
- From an AI assistant: the MCP server’s
make_teststool makes the suite from captured requests (ids,apporhost) and adds it to the API client; withrun: trueit also runs it and returns each failing test. Run it again later withrun_collection. - In CI: with
cloakctl, below.
A failing test after a release usually means the API changed: a field became a string, a status changed, an endpoint got slower. Failures right after making the suite usually mean an expired credential or data that changed since the capture — update the variable, or edit the test.
cloakctl tests make turns a recording into a Postman v2.1 collection file; cloakctl collection run runs it and exits with 1 when a test fails.
# Once: record the journey and make the suite (commit tests.json).cloakctl run --ssl 'api.example.com' --record journey.har --quiet -- npm run test:e2ecloakctl tests make --session journey.har --host api.example.com --out tests.json
# In CI: run it against staging, with a fresh token.cloakctl collection run tests.json --env-var authorization="Bearer $STAGING_TOKEN" --reporter-junit-export results.xml| Option | What it does |
|---|---|
--session FILE / --har FILE |
The recording: a .cloaksession or .har (required) |
--host HOST / --app NAME |
Only this host’s (repeatable) or app’s requests. Naming a host includes it even when it’s an SDK company |
--name TEXT |
The collection’s name |
--out FILE |
Write the collection here (default: standard output) |
--save [--workspace DIR] |
Add it to the API client’s workspace instead |
--run |
Run it straight away and print each step; exit 1 when a test or request fails |
--assert LIST |
Which tests: status, content-type, schema, value, response-time (comma-separated; default all) |
--time-budget N / --min-time-budget-ms N |
The response time budget: the slowest captured time × N (default 3), at least this many milliseconds (default 200) |
--values-from-one-response |
Also assert flags and labels seen only once |
--no-chain |
Send the captured values as they are |
--no-credential-variables / --blank-credentials |
Keep credentials in the headers, or leave the variables empty |
--include-server-errors / --include-third-parties / --one-per-endpoint |
Which requests to keep |
The file holds the captured credential values unless you pass --blank-credentials; keep it private, or pass credentials at run time with --env-var, which takes precedence over the collection’s variables.
Limits
Section titled “Limits”- Tests describe what was captured. A field that was always present is required, and a value that never changed is asserted, even when the API allows otherwise; edit the test when it’s too strict.
- Values that change on every call (timestamps, IDs, tokens) are left out by name and shape; one that slips through fails on the next run — delete its test.
- Only JSON bodies get schema and value tests. A response’s schema comes from the captured responses with the same status.
- Requests run in the captured order. A journey that depends on state the capture didn’t include (an item already in the cart, a user that exists) needs that state on the server.
- Running sends real requests; writes change data each time.