> For the complete documentation index, see [llms.txt](https://docs.drizz.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.drizz.dev/running-tests/test-plans.md).

# Test plans

A test plan is a set of tests plus the settings that run them: app build, devices, dataset, concurrency and a quality gate.

|                    |                                                                                           |
| ------------------ | ----------------------------------------------------------------------------------------- |
| **Platforms**      | Android · iOS (real-device build required)                                                |
| **A plan holds**   | Test cases · execution settings · reports                                                 |
| **App upload cap** | 500 MB per build                                                                          |
| **Runs on**        | Device cloud                                                                              |
| **Watch out**      | Concurrency is capped by available devices *and* by lockable pool size — the smaller wins |

## Prerequisites

* A registered app build — an APK, or an iOS real-device build, under 500 MB
* At least one saved test
* A dataset, if any test in the plan uses `{{variables}}`
* A lockable pool sized to your target concurrency, if any test leases from one

## What a plan holds

Three parts. Each changes independently of the others.

| Part                   | Contents                                                        | Changing it                                                     |
| ---------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- |
| **Test cases**         | Which tests run, and in what order                              | Add, remove or reorder. The underlying test files are untouched |
| **Execution settings** | App build, devices, dataset, concurrency, quality gate          | Change per run without editing a test                           |
| **Reports**            | Results, run history and recordings, aggregated across the plan | Written automatically after every run                           |

## Plan settings

| Setting          | What it controls                                          | Constraints                                                                 |
| ---------------- | --------------------------------------------------------- | --------------------------------------------------------------------------- |
| **Test cases**   | Which tests run, in which order                           | Order is execution order only; it never edits a test file                   |
| **Devices**      | Local, cloud or private, and the model and OS version     | See [Devices](/running-tests/devices.md). If nothing is free, the run waits |
| **Concurrency**  | How many tests run at once                                | Capped by available devices and by lockable pool size — the smaller wins    |
| **Dataset**      | The values every `{{variable}}` in the plan resolves from | Bind at plan level, not in the script. Bind one even if empty               |
| **Quality gate** | The pass threshold the plan must clear to count as passed | A percentage, checked by CI                                                 |
| **App build**    | The registered build the plan installs                    | 500 MB cap. iOS requires a real-device build                                |

## Plan-only capabilities

| Capability                                               | Available locally |
| -------------------------------------------------------- | ----------------- |
| Several tests in one run                                 | ❌                 |
| Parallel execution                                       | ❌                 |
| Cloud and private devices                                | ❌                 |
| CI-triggered runs                                        | ❌                 |
| Dataset-driven runs across many accounts or environments | ❌                 |
| Quality gate on the run result                           | ❌                 |

## App builds

Plans run against a **registered app** — a build uploaded to Drizz. Local runs use whatever is installed on the device; plans do not.

|                                  |                                                           |
| -------------------------------- | --------------------------------------------------------- |
| **Maximum upload size**          | 500 MB                                                    |
| **Android**                      | An APK                                                    |
| **iOS**                          | A **real-device build**.                                  |
| **Downloading a build back out** | Not supported — keep your own copy of anything you upload |

## Devices

A plan runs on local, cloud or private devices. See [Devices](/running-tests/devices.md) for the comparison and for what happens when nothing is free.

## Data

If tests use `{{variables}}`, bind a **dataset** to the plan. Binding happens at plan level, not in the script, so one suite runs against staging and production without an edit.

## Concurrency and quality gates

**Concurrency** is how many tests in the plan run at once. Two ceilings apply on top of the number you set:

* The **devices available** to you at that moment.
* Any **lockable pool** your tests lease from. A 4-row pool means at most 4 parallel runs, whatever concurrency is configured.

**Quality gate** is the threshold a plan must clear to count as passed — for example, pass if 90% or more of tests pass. That is the number a CI job checks. At 100%, a single flaky test blocks every build.

Both are set on the run screen — see [Creating & running a plan](/running-tests/creating-and-running-a-plan.md).

## Common mistakes

<table data-search="false"><thead><tr><th>What you do</th><th>What happens</th></tr></thead><tbody><tr><td>Bundle every flow into one plan</td><td>One failure buries the signal, and a re-run costs the whole suite</td></tr><tr><td>Name plans <code>Plan 1</code>, <code>Plan 2</code></td><td>A failed run doesn't say what it covered. Use <code>Checkout_Regression</code>, <code>Login_Sanity</code></td></tr><tr><td>Set concurrency to 20 with a 4-row lockable pool</td><td>16 runs sit waiting. The pool is the real limit</td></tr><tr><td>Upload an iOS simulator build</td><td>Rejected at plan creation. Plans need a real-device build</td></tr><tr><td>Leave variables in tests with no dataset bound</td><td><code>SET</code> misclassifies and the run goes wrong quietly</td></tr><tr><td>Mix device models within a plan</td><td>Device-specific failures read as flakiness</td></tr><tr><td>Skip the healed-step list</td><td>A step that heals repeatedly has a weak description — see <a href="/pages/WhpvSc0GZjCheriEDXSF">Self-healing</a></td></tr></tbody></table>

## Next

* [Creating & running a plan](/running-tests/creating-and-running-a-plan.md)
* [Devices](/running-tests/devices.md)
* [Reading a report](/reports-and-debugging/reading-a-report.md)

***

*Last updated: 6 August 2026*
