Skip to content

Commit 41542e8

Browse files
committed
wip
1 parent ba30f7e commit 41542e8

1 file changed

Lines changed: 116 additions & 0 deletions

File tree

test-impact-analysis.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
title: Test Impact Analysis (TIA)
3+
description: Test Impact Analysis is a great way to drastically reduce the time it takes to run your test suite by re-running only the tests affected by your latest changes.
4+
---
5+
6+
# Test Impact Analysis (TIA)
7+
8+
Test Impact Analysis is a great way to drastically reduce the time it takes to run your test suite by re-running only the tests affected by your latest changes. The first time you run with `--tia`, Pest records a graph of which tests depend on which files. Every run after that, Pest looks at what you changed, runs only the tests that touched those files, and replays cached results for everything else.
9+
10+
A typical Laravel suite that takes 15 seconds replays in under a second. Edits to a single Blade template re-run a handful of feature tests. Comment-only edits, formatter passes, and README touches re-run nothing at all.
11+
12+
To get started, just add the `--tia` flag to any Pest invocation:
13+
14+
```bash
15+
./vendor/bin/pest --parallel --tia
16+
```
17+
18+
The first run is the **baseline** — Pest enables a coverage driver (PCOV or Xdebug) and records the dependency graph as your tests execute. Expect a small overhead on this run only.
19+
20+
Every subsequent run is a **replay**. Pest compares your working tree against the baseline and re-runs only the tests affected by your changes:
21+
22+
```
23+
Tests: 774 passed (2658 assertions, 7 affected, 767 replayed)
24+
Duration: 0.74s
25+
```
26+
27+
`affected` is the set of tests Pest actually executed. `replayed` is the set whose results were served from cache.
28+
29+
## How Pest Decides What To Run
30+
31+
For each file you've changed, Pest looks for tests that depend on it:
32+
33+
- **PHP source files** — your `app/` classes, controllers, models, helpers — are tracked through the coverage driver. A change to `app/Models/User.php` re-runs only the tests that touched `User`.
34+
- **Migrations** are intersected with the tables each test queried during the baseline. A column rename in `create_users_table.php` re-runs only the tests that queried the `users` table.
35+
- **Inertia pages** under `resources/js/Pages` re-run only the tests that server-side-rendered them.
36+
- **Shared JS components** under `resources/js/Components`, `Layouts`, and friends re-run only the tests whose pages import them — Pest walks Vite's module graph to figure this out.
37+
- **Blade templates** re-run only the tests that rendered them, including renders triggered by browser tests.
38+
- **Anything else** — config files, route files, `.env`, files outside the recorded graph — falls through to a broader pattern. Editing `config/app.php` re-runs every test, because Pest can't statically prove which tests depend on it.
39+
40+
## Cosmetic Edits Don't Run Anything
41+
42+
Pest normalises file content before comparing, so cosmetic changes don't trigger any tests. PHP files have whitespace, line comments, and docblocks stripped before hashing. Blade strips `{{-- … --}}` comments. JS, TS, Vue, and Svelte files have line and block comments removed too.
43+
44+
The result: a comment-only edit, a Prettier reformat, a Pint pass, or a README tweak produces an identical hash, and the file never enters the changed set. Zero tests run.
45+
46+
## Modes
47+
48+
Pest supports a few flags alongside `--tia`:
49+
50+
| Flag | Behaviour |
51+
|---|---|
52+
| `--tia` | Replay if a baseline graph exists, otherwise record. |
53+
| `--tia --fresh` | Discard any existing graph and re-record from scratch. Use this after large refactors or when the graph feels stale. |
54+
| `--tia --refetch` | Force a CI baseline fetch even within the 24-hour cooldown after a previous failed fetch.
55+
56+
## Sharing The Baseline From CI
57+
58+
Recording the baseline locally takes minutes on large suites. Instead, you can have CI record it once per merge to `main`, and every developer downloads the result.
59+
60+
When Pest detects no local graph (or the local graph is out of date), it uses GitHub's CLI to download the latest successful run of a `tia-baseline.yml` workflow's `pest-tia-baseline` artifact. Pest validates the fetched graph against your project state — if it matches, it's adopted. Otherwise it's discarded and a local rebuild proceeds.
61+
62+
Here's a starter workflow you can drop into `.github/workflows/tia-baseline.yml`:
63+
64+
```yaml
65+
name: TIA Baseline
66+
on:
67+
push: { branches: [main] }
68+
schedule: [{ cron: '0 3 * * *' }]
69+
workflow_dispatch:
70+
jobs:
71+
baseline:
72+
runs-on: ubuntu-latest
73+
steps:
74+
- uses: actions/checkout@v4
75+
with: { fetch-depth: 0 }
76+
- uses: shivammathur/setup-php@v2
77+
with: { php-version: '8.5', coverage: xdebug }
78+
- run: composer install --no-interaction --prefer-dist
79+
- run: ./vendor/bin/pest --parallel --tia --coverage
80+
- name: Stage baseline for upload
81+
shell: bash
82+
run: |
83+
mkdir -p .pest-tia-baseline
84+
cp -R "$HOME/.pest/tia"/*/. .pest-tia-baseline/
85+
- uses: actions/upload-artifact@v4
86+
with:
87+
name: pest-tia-baseline
88+
path: .pest-tia-baseline/
89+
retention-days: 30
90+
```
91+
92+
After CI runs, every developer who runs `pest --tia` for the first time on the repo will download this baseline and start replaying immediately, paying no record cost.
93+
94+
## Storage
95+
96+
Pest stores its state at `~/.pest/tia/<project-key>/`, where the project key is derived from your normalised git remote URL — so `git@github.com:foo/bar.git` and `https://github.com/foo/bar` produce the same key. A non-git project falls back to a hash of the project's absolute path.
97+
98+
Sharing state per remote URL means multiple worktrees of the same repository share one cache, while unrelated projects on the same machine stay isolated.
99+
100+
## Custom Watch Patterns
101+
102+
If your project has a directory layout that doesn't match the framework defaults, you can register custom watch patterns in `tests/Pest.php`:
103+
104+
```php
105+
pest()->tia()->watch([
106+
'config/billing/**/*.php' => ['tests/Feature/Billing'],
107+
108+
//
109+
]);
110+
```
111+
112+
Each glob maps to a list of test directories; whenever a matching file changes, every test under those directories is invalidated. Use this when you have specific test directories that genuinely depend on a file Pest can't see otherwise.
113+
114+
---
115+
116+
Now that you've learned how to use Test Impact Analysis to speed up your test suite, let's discuss how to integrate Pest with your continuous integration workflow: [Continuous Integration](/docs/continuous-integration)

0 commit comments

Comments
 (0)