Retries and flaky tests¶
Sometimes, tests fail nondeterministically, which can be quite annoying to developers locally and in CI. cargo-nextest supports retrying failed tests with the --retries
option. If a test succeeds during a retry, the test is marked flaky. Here's an example:
$ cargo nextest run test_flaky_mod_2 --retries 2
Starting 1 test across 2 binaries (11 skipped; run ID: 7859d0ee-55ab-4892-9127-24f4bf805f36, nextest profile: default)
TRY 1 FAIL [ 0.003s] nextest-tests::basic test_flaky_mod_2
--- TRY 1 STDOUT: nextest-tests::basic test_flaky_mod_2 ---
running 1 test
test test_flaky_mod_2 ... FAILED
failures:
failures:
test_flaky_mod_2
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 17 filtered out; finished in 0.00s
--- TRY 1 STDERR: nextest-tests::basic test_flaky_mod_2 ---
thread 'test_flaky_mod_2' panicked at tests/basic.rs:53:9:
Failed because attempt 1 % 2 != 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
TRY 2 PASS [ 0.002s] nextest-tests::basic test_flaky_mod_2
------------
Summary [ 0.021s] 1 test run: 1 passed (1 flaky), 25 skipped
FLAKY 2/3 [ 0.002s] nextest-tests::basic test_flaky_mod_2
$ cargo nextest run test_flaky_mod_2 --retries 2
Starting 1 test across 2 binaries (11 skipped; run ID: 7859d0ee-55ab-4892-9127-24f4bf805f36, nextest profile: default)
TRY 1 FAIL [ 0.003s] nextest-tests::basic test_flaky_mod_2
--- TRY 1 STDOUT: nextest-tests::basic test_flaky_mod_2 ---
running 1 test
test test_flaky_mod_2 ... FAILED
failures:
failures:
test_flaky_mod_2
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 17 filtered out; finished in 0.00s
--- TRY 1 STDERR: nextest-tests::basic test_flaky_mod_2 ---
thread 'test_flaky_mod_2' panicked at tests/basic.rs:53:9:
Failed because attempt 1 % 2 != 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
TRY 2 PASS [ 0.002s] nextest-tests::basic test_flaky_mod_2
------------
Summary [ 0.021s] 1 test run: 1 passed (1 flaky), 25 skipped
FLAKY 2/3 [ 0.002s] nextest-tests::basic test_flaky_mod_2
--retries 2
means that the test is retried twice, for a total of three attempts. In this case, the test fails on the first try but succeeds on the second try. The TRY 2 PASS
text means that the test passed on the second try.
Flaky tests are treated as ultimately successful. If there are no other tests that failed, the exit code for the test run is 0.
Retries can also be:
- passed in via the environment variable
NEXTEST_RETRIES
. - configured in
.config/nextest.toml
.
For the order that configuration parameters are resolved in, see Hierarchical configuration.
Delays and backoff¶
In some situations, you may wish to add delays between retries—for example, if your test hits a network service which is rate limited.
In those cases, you can insert delays between test attempts with a backoff algorithm.
Delays are configuration-only
Delays and backoff can only be specified through configuration. Passing in --retries
via the command line, or specifying the NEXTEST_RETRIES
environment variable, will override delays and backoff specified through configuration.
Fixed backoff¶
To insert a constant delay between test attempts, use the fixed backoff algorithm. For example, to retry tests up to twice with a 1 second delay between attempts, use:
[profile.default]
retries = { backoff = "fixed", count = 2, delay = "1s" }
Exponential backoff¶
Nextest also supports exponential backoff, where the delay between attempts doubles each time. For example, to retry tests up to 3 times with successive delays of 5 seconds, 10 seconds, and 20 seconds, use:
[profile.default]
retries = { backoff = "exponential", count = 3, delay = "5s" }
A maximum delay can also be specified to avoid delays from becoming too large. In the above example, if count = 5
, the fourth and fifth retries would be with delays of 40 seconds and 80 seconds, respectively. To clamp delays at 30 seconds, use:
[profile.default]
retries = { backoff = "exponential", count = 3, delay = "5s", max-delay = "30s" }
This effectively performs a truncated exponential backoff.
Adding jitter¶
To avoid thundering herd problems, it can be useful to add randomness to delays. To do so, use jitter = true
.
[profile.default]
retries = { backoff = "exponential", count = 3, delay = "1s", jitter = true }
jitter = true
also works for fixed backoff.
The current jitter algorithm picks a value in between 0.5 * delay
and delay
uniformly at random. This is not part of the stable interface and is subject to change.
Per-test overrides¶
Nextest supports per-test overrides for retries, letting you mark a subset of tests as needing retries. For example, to mark test names containing "test_e2e"
as requiring retries:
[[profile.default.overrides]]
filter = 'test(test_e2e)'
retries = 2
Per-test overrides support the full set of delay and backoff options as well. For example:
[[profile.default.overrides]]
filter = 'test(test_remote_api)'
retries = { backoff = "exponential", count = 2, delay = "5s", jitter = true }
Note: The
--retries
command-line option and theNEXTEST_RETRIES
environment variable both disable overrides.
JUnit support¶
Flaky test detection is integrated with nextest's JUnit support. For more information, see JUnit support.