How does `detox test --retries` decide what to re-run, and why can relying on it hide real problems?
answer
- re-spawns the runner
- whole failing files, not single tests
- noRetryArgs drops shard
- jest.retryTimes suppresses CLI retries
- 0.5 % per test, 40 % per suite
basics
~20 sdetox test --retries N re-spawns Jest for the test files that failed, re-running each whole file up to N more times. It keeps pipelines green despite flakiness, but a retried pass can hide intermittent product bugs, so retried files must be tracked and fixed.
solid answer
~40 s`--retries N`, or `testRunner.retries` (default `0`), makes the Detox CLI restart the test runner with only the files that had failures, until they pass or the attempts run out. The unit is the file, so passing tests in that file run again; the failed paths replace the default positional args, and `noRetryArgs` drops `shard` by default. If a file uses `jest.retryTimes`, Detox suppresses its own retries unless `retryAfterCircusRetries` is set. Retries make sense because small per-test flakiness compounds across a suite, but a retried pass can hide a real race — like a double booking — so I keep the count low, record failing-test artifacts, and track every retried file until it is fixed.
code
javascript · 10 lines/** @type {Detox.DetoxConfig} */
module.exports = {
testRunner: {
args: { $0: 'jest', config: 'e2e/jest.config.js' },
retries: 1, // re-run failing files once
bail: true, // stop retrying on a permanent suite failure
noRetryArgs: ['shard'], // the default: do not re-shard retried files
},
// devices, apps, configurations ...
};go deeper
Recall that --retries re-runs failing test files and that a retried pass still signals a flaky test.
Explain file-level granularity, how failed paths replace positional args, noRetryArgs, bail and the jest.retryTimes interaction.
Use retries as a tracked stopgap with artifacts, and separate test-data or timing flakiness from intermittent product bugs.
Set a policy for how long a test may stay retried and how retry data feeds the team's quality work.
## What `--retries` actually does `detox test --retries N` (or `testRunner.retries` in `.detoxrc.js`, default `0`) tells the **Detox** CLI to re-spawn the test runner for the **test files that had failures**, until they pass or `N` extra attempts are used up. After the first run Detox prints the failing files and restarts Jest with only those paths: ```bash detox test -c android.emu.release --retries 2 # ... There were failing tests in the following files: # 1. e2e/booking.test.js # Detox CLI is going to restart the test runner with those files... ``` Key mechanics: 1. **Granularity is the file.** The whole file re-runs, including its tests that already passed. A file with fifteen tests and one flaky test repeats all fifteen. 2. **Positional arguments are replaced.** On a retry, the failed file paths override `testRunner.args._`, the default positional arguments. 3. **Some arguments are dropped.** `testRunner.noRetryArgs` (default `['shard']`) lists runner arguments removed on retries, so a sharded run does not re-shard the short list of failed files. 4. **`testRunner.bail`** (default `false`) stops further retries when a permanent test-suite failure is reported; it has no effect when retries are off. 5. **Jest's own retries win by default.** If a test file uses `jest.retryTimes(count)`, Detox suppresses its CLI retry mechanism, because both together can multiply test time on a flaky suite. `testRunner.jest.retryAfterCircusRetries: true` re-enables both. | Mechanism | Retries | Scope | Set by | |---|---|---|---| | Detox CLI retries | whole test files | per `detox test` invocation | `--retries`, `testRunner.retries` | | Jest `retryTimes` | individual tests | inside one Jest run | the test file | ## Why retries can hide real problems The Detox flakiness guide gives the arithmetic that makes retries tempting: 100 tests that each fail 0.5 % of the time without a bug make the whole suite fail about 40 % of the time (`1 - (1 - 0.005)^100`). A retry turns most of those runs green. But a green retried run says only that the failure is intermittent, not that it is harmless: - **Real races pass on retry.** In the hotel-booking app, a double-tap on "Book" that sometimes creates two reservations is an intermittent product bug; a retry hides it from the pipeline and from users' bug reports alike. - **Slow suites get slower.** Each retry re-runs whole files, so a flaky file near the end of a long suite can add minutes to every pipeline. - **The signal decays.** If retries are always on and nobody reads which files were retried, the flaky list only grows. ## Using retries responsibly - Keep `--retries` low (one or two) and **treat any retried pass as a finding**: record the retried files from the Detox output and track them. - Pair retries with **artifacts for failing tests**, so the first failure still leaves logs, screenshots and video even when the retry passes. - Prefer fixing the cause — test data shared between parallel files, a missing wait for an unsynchronized animation, a timeout sized for a laptop — over raising the retry count. - Do not mix Detox CLI retries and `jest.retryTimes` without deciding which one you want; the default already prevents both from running. ## Setting retries per environment - **Locally:** keep `retries` at `0` so a flaky test fails loudly while you are working on it. - **On CI:** pass `--retries 1` (or `-R 1`) in the pipeline command, or set `testRunner.retries` inside the CI configuration only, since a configuration can override the global `testRunner` section. - **Reading the output:** the "There were failing tests in the following files" block names every retried file; turning that list into a tracked report is what keeps retries honest. - **Budget:** each retry re-runs whole files, so estimate the worst case — the slowest files times the retry count — before raising it. ## What a strong answer sounds like "Retries re-run whole failing files, not single tests; they are a stopgap that keeps the pipeline usable while flaky tests are tracked and fixed, and they must not hide intermittent product bugs." Interviewers are listening for that last clause.
- What happens if a test file calls jest.retryTimes(2) and the CLI also has --retries 2?Detox detects Jest's retry API and suppresses its own CLI retries, because both together can multiply run time on flaky files. Jest retries the individual failing tests; the Detox CLI does not re-spawn the file afterwards unless `testRunner.jest.retryAfterCircusRetries` is set to `true`.
- Why does noRetryArgs remove shard by default?A sharded first run splits the suite across machines. On a retry the runner receives only the few failed files, and applying the original shard split to that short list could drop them entirely. Removing `shard` makes the retry run every failed file on the machine that saw it fail.
Retries are like re-sending a parcel that went missing: the customer gets it in the end, but if nobody logs how often parcels vanish, the leaky depot never gets fixed.
saying these in an interview costs you the question
- --retries re-runs only the single failed test inside a file.
- A test that passes on retry proves the app is fine.
- Higher retry counts are a free way to fix flakiness.
- Detox and jest.retryTimes retries always stack by default.
- Retries make failing-test artifacts unnecessary.