Recipe
Fix a test that fails only sometimes
Use when: a test fails now and then in the full suite, or passes only when another test has run first
Goal: tests that each set up their own files, and pass alone, together and in any order
Taught in Fixtures and organization
Quick recipe
-
1
Run the whole suite a few times with
cargo test -- --test-threads=1. Then run the failing test alone, by name.Example
In the
orderedproject,cargo test shares_load_backfails alone withNo such file or directory. It only passed before becausesaves_the_shareshad just made the file.- it passes with one thread and fails with many — two tests are racing each other.
- it passes in the suite and fails alone — it needs another test to run first.
Why:
cargo testruns tests at the same time, in no fixed order, so a shared file is the usual cause. -
2
Search the tests for a file name, folder or path that more than one test uses.
Example
Both tests in
ordereduseshares.txtin the package folder. One writes it. The other reads it and deletes it.Why: two tests that touch one path can run in either order, or both at once.
-
3
Add
tempfileunder[dev-dependencies]. At the top of each test, make a fresh folder and keep it in a variable. Build every path inside that folder.Example
let dir = tempfile::tempdir()?;and thenlet path = dir.path().join("shares.txt");. Two tests can both use the nameshares.txtnow, each in its own folder.Why: each
tempdir()call makes a new folder with a unique name, and dropping theTempDirdeletes it when the test ends. -
4
A test that reads a file writes that file first, itself. Put setup that many tests share in a helper function, such as
scratch().Example
saved_shares_load_backcallssave_shares(&path, &[334, 333, 333])?itself, and only thenload_shares(&path)?. It no longer relies on another test.Why: a test that builds its own world gives the same result alone, in any order, and in parallel.
-
5
Run plain
cargo testa few times, and the fixed test alone. Take--test-threads=1back out of any script that used it as a cure.Example
cargo testandcargo test saved_shares_load_backboth pass, run after run.Why: one thread only hid the problem; the fix has to hold with many threads.
Common mistakes
- Keeping
--test-threads=1as the fix. It slows every run and hides the shared file. - Renaming tests so they sort in the "right" order. Tests run at the same time, and can be run alone.
- Writing
tempfile::tempdir()?.path().join(...)in one line. The folder is deleted at the;. - Using one fixed file name in a shared folder. Two tests still collide on it.