# Make tests from traffic

> 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.

Source: https://docs.subcloak.com/cloak-proxy/testing/make-tests/

Added in Cloak Proxy 0.4.0.

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.

*[Screenshot: The Make Tests window for 13 selected requests from a ShopApp checkout: 12 requests, 72 tests, 1 chained value and 1 skipped. Each row shows the step number, method and path, chips such as → orderId on POST /v2/orders and {{orderId}} on POST /v2/payments and GET /v2/orders/{id}, the number of tests with an icon per kind, and the captured status and time. The selected POST /v2/orders shows its tests — Status is 201, Content-Type is application/json, Body has the shape of 2 captured responses, $.order.status is pending, $.order.total is 54.9, Responds within 750 ms, orderId is in the response — the chained value orderId read from body $.order.id for steps 10 and 11, and the generated test script.. 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.]*

## Make a suite

1. Capture the journey you want to test: use the app, or run your end-to-end tests through Cloak Proxy.

2. 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…**

3. Check the steps and their tests. Change the **Scope** or the **Options** and the suite is made again straight away.

4. Click **Try It** to run the suite once against the real servers and see which tests pass, without saving anything.

5. Click **Add to API Client** (<kbd>⌘</kbd> <kbd>↩</kbd>). The collection is added to the [API client](https://docs.subcloak.com/cloak-proxy/api-client/overview/) and its [collection runner](https://docs.subcloak.com/cloak-proxy/api-client/run/collection-runner/) opens.

> **On Linux:**
> Right-click requests and choose **Make Tests from N Requests…**, or right-click an app or domain in the sidebar and choose **Make Tests from _name_…**. **Add to API Client** is <kbd>Ctrl</kbd> <kbd>Enter</kbd>, and <kbd>Esc</kbd> stops **Try It**.

> **Running sends real requests:**
> **Try It**, the collection runner and `cloakctl` send the requests to the real servers. A suite with `POST`, `PUT`, `PATCH` or `DELETE` requests changes data again each time it runs, so Cloak Proxy asks before a trial run that includes them. Run suites against a test or staging system you're allowed to change.

## 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](https://docs.subcloak.com/cloak-proxy/capture/ssl-proxying/) to see them), requests still in progress and requests that got no response;
- images, scripts, styles, fonts and media, and CORS preflight `OPTIONS` requests;
- 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](https://docs.subcloak.com/cloak-proxy/insights/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

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

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](https://docs.subcloak.com/cloak-proxy/api-client/run/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

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.

```js title="POST /v2/orders, post-response script (shortened)"
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

- **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](https://docs.subcloak.com/cloak-proxy/api-client/run/collection-runner/).
- **From an AI assistant:** the [MCP server](https://docs.subcloak.com/cloak-proxy/automation/mcp-server/)'s **`make_tests`** tool makes the suite from captured requests (`ids`, `app` or `host`) and adds it to the API client; with `run: true` it also runs it and returns each failing test. Run it again later with `run_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.

## In CI

`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.

```bash title="Terminal"
# Once: record the journey and make the suite (commit tests.json).
cloakctl run --ssl 'api.example.com' --record journey.har --quiet -- npm run test:e2e
cloakctl 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

- 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.

## Related

  - [Collection runner](https://docs.subcloak.com/cloak-proxy/api-client/run/collection-runner/): Run a collection, read the results and export reports.
  - [Replayable flows](https://docs.subcloak.com/cloak-proxy/api-client/run/replayable-flows/): Replay a captured journey with chained values as a flow.
  - [Scripts and tests](https://docs.subcloak.com/cloak-proxy/api-client/scripts-and-tests/): The pm API, assertions and packages.
  - [Release diff](https://docs.subcloak.com/cloak-proxy/testing/release-diff/): Compare two recordings of the same journey.
