jasonmccreary/phpunit-tia
phpunit-tia brings Test Impact Analysis to PHPUnit: it tracks which tests cover which code, then runs only the tests affected by your uncommitted changes. Skip the full suite, get faster feedback, and keep confidence in your changes.
jasonmccreary/phpunit-tia adds Test Impact Analysis to a PHPUnit suite: it records which source files each test file touches, then on later runs replays only the tests whose recorded dependencies changed. It is a dev-only dependency, so install it with --dev and keep it out of production builds.
Minimal path:
composer require --dev jasonmccreary/phpunit-tiaphpunit.xml.dist (the README shows the exact <extensions> block — copy it verbatim, including the bootstrap attribute if listed).phpunit/artisan test directly. You should see a short summary of affected test files and a much smaller executed set.Look first at the package's config file (published via the install/vendor:publish step) — the groups/selection keys are the ones that change behaviour most. Second, look at the recorded graph artifact and where it is written; that file is the thing you will cache in CI.
First use case: a PR pipeline. Record the graph on the base branch, replay against it on the PR. That is the highest-leverage adoption because it is where full-suite time actually hurts.
Baseline-first, then replay. The package refuses to replay without a recorded baseline (v0.5.0 made this explicit). Treat graph recording as a pipeline stage of its own: one job does a full run and publishes the graph artifact; downstream jobs consume it. Locally, the same applies — record once after a composer update or a large merge, then work incrementally.
CI caching keyed by the base ref. Key the graph cache on the target branch, not the feature branch, so a PR replays against what main last knew about. Fall back to a full run on cache miss rather than failing the build; a missing graph should degrade to "run everything".
Parallel suites. v0.5.0 fixed graph recording under ParaTest. If you run --parallel, upgrade — earlier versions recorded an unreliable graph and produced false negatives (tests that should have run, silently skipped). Verify by diffing the recorded graph from a parallel run against a serial run for the same commit.
Group selection is deliberately conservative. Selecting a subset via configured groups is treated as a full run. This is correct behaviour — a partial run must not be allowed to overwrite the graph with incomplete dependency data — but it means "fast" group-filtered runs are not fast. Don't build a workflow that assumes otherwise.
Custom edges via the Edges contract. If some of your test dependencies are invisible to the analyser (see Gotchas), implement the Edges contract to declare them explicitly. The contract now exposes every known test file, so you can enumerate test files and attach edges without reaching into internals. This is the supported extension point — prefer it over hand-editing the graph.
Tooling on top of testsLinkedTo(). The reverse index (test indexed by source as of #26) makes it cheap to answer "which tests cover this class?" Use this to build a PR comment bot, a --changed-since helper, or an editor integration. This is the cleanest way to add value without forking the replay logic.
Watch the run summary. Every run now prints affected test files. Log it. When someone asks "why didn't my test run?", that summary plus the changed-file list is the whole debugging story.
Laravel-specific integration. For a typical app, the analyser sees PHP source changes well and non-PHP changes poorly. Pair the package with a small always-run set — route files, config, migrations, Blade fixtures — and keep a nightly or pre-merge full run as the correctness backstop.
config()-driven class names, dynamic class_exists checks, views, translation files, fixtures, and DB seeders are not reliably edge-tracked. Declare them via the Edges contract or in an always-run list. False negatives are the dangerous failure mode; false positives only cost time.--filter, --testsuite, groups) cannot persist state. The package guards this for its own group selection; your wrapper needs to as well.setUpBeforeClass side effects from siblings will surface here first.composer.json, and treat custom Edges implementations as code you'll revisit on minor bumps.How can I help you explore Laravel packages today?