Skip to main content
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

1

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

Your test runs

Values the pre-requests returned are available to it. See Remembering values from the response.
3

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

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:
every one of these is a usable name: 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.
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.

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:
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>}:
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 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 at the moment it builds the request:
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: 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

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.