> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mobileboost.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Setup and cleanup calls

> Give a test the HTTP calls that seed its state before it runs, and the ones that clean up after it

Plenty of tests need the world arranged before the app opens: an account that
exists, a listing to reply to, a feature flag turned on, a cart emptied. Doing
that through the UI makes the test about the setup, and doing it by hand makes
the test unrepeatable.

A test can carry **pre-requests**, which the runner makes before the test
starts, and **teardown requests**, which it makes once the test has finished.
Both run on the same device session as the test they belong to, so a value a
pre-request produced is available to the test that follows it.

## When they run

<Steps>
  <Step title="Pre-requests, before the test">
    In the order you list them, before the test starts. A test that retries
    resets the app first and then makes them again, so every round begins
    against state that was created for it.
  </Step>

  <Step title="Your test runs">
    Values the pre-requests returned are available to it. See
    [Remembering values from the response](#remembering-values-from-the-response).
  </Step>

  <Step title="Teardown requests, after the test">
    In reverse order, the way nested resources need: the last thing created is
    the first thing removed. They run after every round and at the end of the
    run, whether the test passed or failed.
  </Step>
</Steps>

A pre-request that fails stops the run there, with the status and response your
API answered in the run log. That is deliberate: a test whose account was never
created fails later on a login screen, in a way that reads as a broken app.

A teardown that fails never changes a verdict. It is logged with the host and
status and the run's result stands.

## Defining them

Both are properties of a test, set through the API, the MCP server, or the
Platform app.

```bash theme={null}
curl -X PATCH "https://api.mobileboost.io/tests" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "testId": "tst_4417",
    "preRequests": [
      {
        "curlCommand": "curl -X POST '\''https://api.acme-staging.io/qa/fixtures'\'' -H '\''content-type: application/json'\'' -H '\''authorization: Bearer ${ACME_QA_TOKEN}'\'' --data-raw '\''{\"accountType\":\"PRIVATE\"}'\''",
        "delayBeforeRequest": 0,
        "valuesToRemember": ["user.email", "user.password", "ad.id", "ad.title"],
        "throughTunnel": true
      }
    ],
    "teardownRequests": [
      {
        "curlCommand": "curl -X DELETE '\''https://api.acme-staging.io/qa/ads/${MB_PRE_AD_ID}'\''",
        "runOn": "always",
        "throughTunnel": true
      }
    ]
  }'
```

| Field                | Meaning                                                                                                                            |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `curlCommand`        | The call to make, written as curl. See [what a curl may contain](#what-a-curl-may-contain).                                        |
| `delayBeforeRequest` | Seconds to wait before sending, `0` to `60`. Use it when your API needs a moment to make what the previous call created queryable. |
| `valuesToRemember`   | What to read out of the response. See below.                                                                                       |
| `throughTunnel`      | Send through the run's MobileBoost Local tunnel. Defaults to `true`.                                                               |
| `runOn`              | Teardown only: `always` (the default), `passed`, or `failed`.                                                                      |

<Note>
  `delayBeforeRequest` is **seconds** here. A QA Studio test interprets the same
  field as milliseconds, so a value copied from one product to the other needs
  converting.
</Note>

## Remembering values from the response

Name what you need out of the JSON body and the test can read it. A name is a
JSON key, a path into the body, or a path that indexes an array along the way.

Given this response:

```json theme={null}
{
  "user": { "id": 51, "email": "seller.4471@test.de", "password": "s3cret" },
  "ad": { "id": 4417, "title": "Bike, Berlin" },
  "ads": [{ "id": "a-1" }, { "id": "a-2" }]
}
```

every one of these is a usable name:

| `valuesToRemember` entry | The test reads      | Value                 |
| ------------------------ | ------------------- | --------------------- |
| `user.email`             | `MB_PRE_USER_EMAIL` | `seller.4471@test.de` |
| `user.id`                | `MB_PRE_USER_ID`    | `51`                  |
| `ad.id`                  | `MB_PRE_AD_ID`      | `4417`                |
| `ads.0.id`               | `MB_PRE_ADS_0_ID`   | `a-1`                 |

You do not have to flatten your API to fit. Nesting is supported, and so is
indexing into a list for a create call that answers with a collection.

**The variable is the whole path**, upper-cased and joined with underscores, so
`user.id` and `ad.id` are two different values on the same test. Names have to
be unique: two entries that would produce one variable are refused when you save
the test, rather than one of them silently winning at run time.

Two things end a run rather than passing a blank through:

* a name the response has no value for, and
* a name that lands on an object, a list, or `null`. A remembered value becomes
  an environment variable, so it has to be something a test can be given: point
  at `ad.id`, not at `ad`.

<Note>
  Values that were remembered before September 2026 were named after the last
  segment alone, so `user.email` produced `MB_PRE_EMAIL`. Existing tests keep
  working: wherever that shorter name is still unambiguous, it is written
  alongside the full one. New tests should use the full path.
</Note>

## Reading a remembered value in the test

Every value is **different on every round and every run**. Read it; never copy a
value you saw into the test file, or the test passes today and fails tonight.

In a generated pytest test, take the `pre_request_values` fixture:

```python theme={null}
def test_reply_to_an_ad(driver, pre_request_values):
    email = pre_request_values["USER_EMAIL"]
    ad_id = pre_request_values["AD_ID"]
```

The same values are in the environment as `MB_PRE_USER_EMAIL` and
`MB_PRE_AD_ID`, which is what a helper module that takes no fixtures should
read.

## Cleaning up

A teardown addresses what a pre-request produced with `${MB_PRE_<NAME>}`:

```bash theme={null}
curl -X DELETE "https://api.acme-staging.io/qa/ads/${MB_PRE_AD_ID}"
```

Naming a value that no pre-request on the test remembers is refused when you
save it, with a list of what the test does produce.

Use `runOn` to keep evidence behind after a failure: `"runOn": "passed"` deletes
the fixture only when the test passed, and leaves it in place for you to look at
when it did not. A conditional teardown runs once, at the end of the run, where
the result is known. `always` runs after every round as well.

Cleanup is handled for the test, so a generated test file should not make
teardown calls of its own. It would run them twice.

## Reaching an internal service

Pre-requests are made by MobileBoost, not by the device, so a public endpoint
needs nothing special. An endpoint inside your own network needs the run to name
a [MobileBoost Local](/test-agent/local-testing) tunnel: the call then travels
down it and is made from inside your network.

`throughTunnel` defaults to `true`, which is what a setup call against a staging
API wants. A run that has no tunnel attached fails the pre-request with a
message saying so. Set it to `false` for a genuinely public call, such as a
third-party token endpoint.

## Using a secret in a curl

Write `${VARIABLE}` anywhere in the command and the runner substitutes it from
your organisation's [environment variables](/qa-studio/environment-variables) at
the moment it builds the request:

```bash theme={null}
curl -X POST "https://api.acme-staging.io/qa/fixtures" \
  -H "authorization: Bearer ${ACME_QA_TOKEN}"
```

The stored test holds the reference, never the resolved value, and the value is
scrubbed out of run logs and reports. A name that has no environment variable
behind it fails the pre-request by name, rather than sending the literal
`${ACME_QA_TOKEN}` to your API.

Names beginning with `MB_` are reserved, which is why a remembered value can
never collide with one of your own variables.

## What a curl may contain

The command is parsed into the HTTP request it describes; it is never run as a
shell command. Anything outside the accepted grammar is refused by name when you
save the test.

**Accepted:** `-X/--request`, `-H/--header`, `-d/--data`, `--data-raw`,
`--data-binary`, `--data-ascii`, `-u/--user`, `--url`, `--max-time`,
`--connect-timeout`, `-k/--insecure`, `-L/--location`, `-s/--silent`,
`-S/--show-error`, `-i/--include`, `-f/--fail`, `--compressed`, `--globoff`.

**Refused, with the reason:**

| Flag                                   | Why                                               |
| -------------------------------------- | ------------------------------------------------- |
| `-o`, `-O`, `--output`                 | writes the response to a file                     |
| `-T`, `-F`, `--form`, `--data @file`   | uploads a file                                    |
| `-K`, `--config`                       | reads flags from a file                           |
| `-x`, `--proxy`, `--preproxy`          | sets a proxy, which the tunnel already decides    |
| `--unix-socket`                        | talks to a local socket rather than the network   |
| `-w`, `--write-out`                    | runs an output template                           |
| `--trace`, `--trace-ascii`, `-D`       | writes to a file                                  |
| `-b`, `-c`, `--cookie`, `--cookie-jar` | reads or writes a cookie jar                      |
| `-E`, `--cert`, `--key`, `--cacert`    | loads a certificate                               |
| `--next`                               | sends more than one request from a single command |
| `-C`, `--continue-at`                  | resumes a transfer                                |

The URL has to be `http` or `https`, and written out in full: a scheme that
arrives through a `${VARIABLE}` is refused, so the host a call reaches is always
visible in the stored test.

## Limits

|                                  |                 |
| -------------------------------- | --------------- |
| Pre-requests per test            | 5               |
| Teardown requests per test       | 5               |
| Length of one curl command       | 8192 characters |
| `delayBeforeRequest`             | 0 to 60 seconds |
| Time one request may take        | 30 seconds      |
| Response read looking for values | 256 KB          |

A response only has to be JSON if the entry remembers something from it. A
setup call that resets a tenant and returns nothing is perfectly valid.

## QA Studio

QA Studio tests have pre-requests too, with the same curl grammar and the same
dot-and-index syntax for values. Two differences:

* a remembered value is addressed there by the **last segment** of its name, in
  a test step as "the remembered email" and in a later curl as `{{email}}`,
  rather than by the full `MB_PRE_USER_EMAIL`;
* `delayBeforeRequest` is in milliseconds.

See [Network requests](/qa-studio/network-requests).
