# Running only affected tests

Several tools run fewer tests after a change: some pick the tests a change could reach, from git and the import graph, and others reuse results they recorded. hushrun's test reuse is the second kind: it leaves out each test that already passed on the files it reads, and shares that across laptops, CI and agents' sandboxes.

| Tool | Picks by | Smallest unit | Remembers a pass | Across machines |
|---|---|---|---|---|
| Jest `--onlyChanged`, `--findRelatedTests` | git changes and the static import graph | Test file | No | No |
| Vitest `--changed`, `vitest related` | Changed files and static imports | Test file | No | No |
| pytest-testmon | Which code each test ran, through Coverage.py | Test | Yes, in `.testmondata` | Through its tmnet service |
| `go test` | The test binary, the files tests open and the variables they read | Package | Yes, on the machine | No |
| Nx `affected` | git changes and each task's inputs | Task, such as `ui:test` | With Nx's cache | With Nx Cloud |
| Bazel | Each target's declared inputs | Test target | Yes | With a remote cache |
| Develocity Predictive Test Selection | A model trained on past changes and results | Test | It weighs past results | Yes |
| hushrun `tests: reuse` | The files each test file, or each test, read when it passed | Test, or test file | Yes | Through your organization's backend |

## Tools that pick from a change

- **Jest.** [`--onlyChanged`](https://jestjs.io/docs/cli) runs the tests related to changed files in a git or Mercurial repository, and needs a static dependency graph, with no dynamic requires. `--changedSince` compares with a branch or commit, and `--findRelatedTests` takes a list of source files, which suits a pre-commit hook.
- **Vitest.** [`--changed`](https://vitest.dev/guide/cli) runs the tests affected by changed files, and `vitest related` the tests that cover a list of source files. Both follow static imports, not dynamic ones.
- **Nx.** [`nx affected`](https://nx.dev/docs/features/ci-features/affected) runs each task whose inputs a change touches, and the tasks that read their outputs. It selects whole tasks, such as a project's `test`, not single tests. In GitHub Actions it needs `fetch-depth: 0`.
- **Turborepo.** [`turbo run --affected`](https://turborepo.dev/docs/reference/run) compares `main` with `HEAD` and, by default, runs every task of each package with a changed file. With too shallow a clone, every package counts as changed.

These need no record of earlier runs, so they work on a fresh clone. But they don't know what passed: a related test runs again even when it passed on the same code an hour ago, on another machine.

## Tools that remember results

- **pytest-testmon.** [testmon](https://testmon.org/) records which code each test ran, with Coverage.py, and on the next `pytest --testmon` selects only the tests affected by changes. Its data lives in `.testmondata`, or in its tmnet cloud storage with `--tmnet`. It doesn't track static files such as text or XML, or services over the network, and it always reruns tests that failed last time.
- **go test.** [In package list mode](https://pkg.go.dev/cmd/go#hdr-Test_packages), such as `go test ./...`, Go caches a package's successful result and replays it when the test binary and the flags match, and the files the tests opened in the module and the environment variables they read haven't changed. `-count=1` turns it off. It's on one machine.
- **Bazel.** With [`--cache_test_results=auto`](https://bazel.build/docs/user-manual), the default, Bazel reruns a test only when it or its dependencies changed, it's marked `external`, `--runs_per_test` asks for more runs, or it failed. With a [remote cache](https://bazel.build/remote/caching), results are shared. Every test target needs its inputs declared in `BUILD` files. See [hushrun and Bazel](https://hushrun.com/docs/compare/bazel.md).
- **Develocity Predictive Test Selection.** [An add-on of Develocity](https://docs.develocity.ai/predictive-test-selection/) for Gradle and Maven tests on the JUnit Platform. A model trained on your builds' changes and results picks the tests likely to matter, at a level of confidence you choose.

## How hushrun's test reuse differs

With [`tests` set in `hushrun.yaml`](https://hushrun.com/docs/tests.md), hushrun records the files each test file, or each test, read when it passed. After a change, it leaves out the tests whose files didn't change, and runs the rest:

```text
hushrun: unit: 3 of 101 tests run, in 2 of 2 test files; 98 already passed on the files they read, and 2 more that already passed run anyway, as a check
```

- **Shared.** A pass recorded on a laptop, in CI or in an agent's sandbox lets the others leave that test out, through your organization's backend.
- **No graph to describe.** It works from what each test read, so there are no `BUILD` files and no task inputs to declare.
- **Many runners, named.** pytest, Vitest, Jest 29 and 30, Go, Gradle (Java, Kotlin, Android's local unit tests), Maven, `swift test` and CTest, test by test from hushrun 0.16.0; whole test files for pytest, Vitest and Go from 0.13.0. Some need one line in their config: [What your runner needs](https://hushrun.com/docs/tests.md#runners). Rust's `cargo test` reuses whole test binaries.
- **Safety checks.** A small share of the tests it could leave out run anyway, and a test that fails though it passed on the same files suspends reuse for the check until a person resumes it ([What keeps it safe](https://hushrun.com/docs/tests.md#safe)).
- **First the whole check.** Before any test, a check whose files all passed already skips as a whole ([How it works](https://hushrun.com/docs/run.md)).

Where others are better:

- **Environment variables.** `go test`'s cache counts the variables tests read. hushrun doesn't: a test that depends on one needs [a check name per variant](https://hushrun.com/docs/run.md#limits).
- **A change outside the sources runs everything.** A README, a lockfile or a CI file may be read by any test, so changing one runs them all, unless you list it in `tests.ignore` ([The trades](https://hushrun.com/docs/tests.md#trades)). Jest and Vitest follow imports, so a file no test imports selects no test.
- **Off by default, per check.** Start each check in `observe` and read what it would have left out before you turn on `reuse`.
- **Some set-ups can't be watched,** such as Vitest with coverage on, or Jest with coverage on. hushrun then runs every test, and says why ([Limits](https://hushrun.com/docs/tests.md#limits)).

## Which to choose

- **While you work on one machine,** Jest's `--onlyChanged`, Vitest's `--changed` or pytest-testmon give fast feedback with nothing to set up beyond a flag or a plugin.
- **In a monorepo already on Nx, Turborepo or Bazel,** use their affected runs and caches. hushrun can wrap the whole run, so code that passed runs nothing at all.
- **When laptops, CI and agents run the same suite,** hushrun's test reuse shares what passed between them, for the runners it supports.

For the setup per runner, see [Run only the tests a change touches](https://hushrun.com/docs/guides/run-only-changed-tests.md).

## Sources

Checked on 2026-11-14:

- [Jest CLI](https://jestjs.io/docs/cli)
- [Vitest CLI](https://vitest.dev/guide/cli)
- [pytest-testmon](https://testmon.org/)
- [go test: test caching](https://pkg.go.dev/cmd/go#hdr-Test_packages)
- [Nx: run only affected tasks](https://nx.dev/docs/features/ci-features/affected)
- [Turborepo: run](https://turborepo.dev/docs/reference/run)
- [Bazel user manual](https://bazel.build/docs/user-manual) and [remote caching](https://bazel.build/remote/caching)
- [Develocity: Predictive Test Selection](https://docs.develocity.ai/predictive-test-selection/)

## Related

- [Reuse tests one by one](https://hushrun.com/docs/tests.md)
- [Cache test results across machines](https://hushrun.com/docs/guides/cache-test-results.md)
- [hushrun and the alternatives](https://hushrun.com/docs/compare.md)
