Help us shape the future of Go! Take the 2026 Go Developer Survey

fast-unit

command module
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2026 License: MIT Imports: 13 Imported by: 0

README

fast-unit

Run a PHPUnit suite ~4x faster by splitting test classes across parallel workers that each boot PHP once and run many classes in a single process.

Ships as prebuilt Go binaries fetched on first run — no Go toolchain needed. Runs on PHP 7.2+ (only the tiny downloader shim is PHP; the runner is a version-agnostic native binary), so it accelerates legacy suites too.

While it runs it prints a live weight-based progress line:

progress:  73% (9/12 chunks)

Progress ticks per finished chunk (chunk weight approximates runtime). It cannot go finer, because each worker runs its whole chunk in one warm PHP process -- that single boot is the speedup.

Install

composer require --dev tomasvotruba/fast-unit

Usage

vendor/bin/fastunit -p 12                          # whole suite
vendor/bin/fastunit -p 8 rules-tests/CodeQuality   # a subtree

On first run the shim downloads the matching binary for your OS/arch from the GitHub release, verifies its sha256, caches it, and execs it.

Flags
Flag Default Meaning
-p CPU count parallel workers
-bin vendor/phpunit/phpunit/phpunit phpunit entry script
-weight fixtures class weighting: fixtures | methods | size
-tmp-isolate true give each worker its own TMPDIR
-php php php interpreter

Positional args are the directories to scan (default rules-tests tests).

Environment overrides
Var Effect
FASTUNIT_BINARY=/path use this binary, skip all download logic (CI prebuild / airgap)
FASTUNIT_VERSION=vX.Y.Z pin a specific release tag

Why it is faster

Bootstrap dominates a class's run, not the assertions. A tool that spawns a fresh process per class pays the container-build cost once per class. fast-unit splits classes into N chunks balanced by weight, and each worker runs its whole chunk in one warm process — so the container is built N times, not once per class.

Weighting modes
  • fixtures — count sibling Fixture/*.php.inc files. Best for Rector rule tests, which iterate one assertion per fixture.
  • methods — count function test* and #[Test] occurrences. Good for generic PHPUnit suites.
  • size — file byte size. Cheap fallback.

Test impact analysis (-tia)

Run only the tests whose code actually changed:

vendor/bin/fastunit -tia rules-tests tests

How it works, per class:

  1. A per-class PHPUnit coverage run records the exact set of source files each test executes (needs the pcov or xdebug driver). That map, plus a sha256 snapshot of every source and test file, is cached under .fastunit-cache/.
  2. On the next run, a class is re-run only if its own file, a sibling fixture, or one of its covered source files changed. Impacted classes run with coverage again, so the map self-heals.
  3. Fail-open by design: a class with no map entry, or a changed source file that no map knows about, runs everything. TIA never silently skips an unknown dependency.
TIA: 1 changed files, 1 impacted, 10 skipped   # edited one rule
TIA: 0 changed files, 0 impacted, 11 skipped   # nothing changed -> instant

Flags: -src (comma-separated source dirs, default src,rules), -tia-cache (cache dir, default .fastunit-cache).

Add .fastunit-cache/ to your .gitignore -- it is a local, machine-specific cache.

Isolation

With -tmp-isolate (default on), each worker gets its own TMPDIR so tools that cache under sys_get_temp_dir() (e.g. Rector's rector_cached_files) do not race across parallel processes. Turn it off for suites that do not need it.

Note: sharing one PHP process across classes can surface latent global-state leaks between test classes (static parameter providers, etc.). Reset such state in tearDownAfterClass().

License

MIT

Documentation

Overview

Command fastunit runs a PHPUnit suite in parallel by splitting test classes into N balanced "warm" chunks: each worker boots PHP once and runs many test classes in a single process, so the container is built N times instead of once per class (as tools that spawn a process per chunk do).

Balancing is by fixture count by default, since Rector rule tests iterate one assertion per .php.inc fixture, so fixture count approximates a class's runtime. Other weighting modes (methods, size) suit generic PHPUnit suites.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL