Skip to content

Commit 34d6b1d

Browse files
committed
feat: time based sharding
1 parent 25b67a5 commit 34d6b1d

3 files changed

Lines changed: 113 additions & 3 deletions

File tree

cli-api-reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ In the preceding chapters of the Pest documentation, we have covered numerous CL
5555
## Execution
5656

5757
- `--parallel` Run tests in parallel.
58+
- `--shard <index/total>`: Run only the given shard of tests (e.g. `--shard=1/5`). Uses time-balanced distribution when `tests/.pest/shards.json` exists.
59+
- `--update-shards`: Update `tests/.pest/shards.json` with test timing data for time-balanced sharding. Can be combined with `--parallel`.
5860
- `--update-snapshots`: Update snapshots for tests using the "toMatchSnapshot" expectation.
5961
- `--globals-backup`: Backup and restore $GLOBALS for each test.
6062
- `--static-backup`: Backup and restore static properties for each test.

continuous-integration.md

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,71 @@ To shard your tests, you can use the `--shard` option when running Pest. For exa
191191
./vendor/bin/pest --shard=1/5
192192
```
193193

194-
To implement test sharding in your CI configuration, you can create multiple jobs that run different shards of your test suite. Here is an example of how to do this with GitHub Actions:
194+
By default, Pest splits tests evenly by **count** — each shard gets roughly the same number of test files. This works well when all tests take similar time, but can create imbalanced shards when some tests (like payment processing or report generation) are significantly slower than others.
195+
196+
### Time-Balanced Sharding
197+
198+
For better shard balance, Pest can distribute tests based on their **actual execution time** using the `--update-shards` option. This ensures each shard takes roughly the same wall-clock time, minimizing how long your slowest CI job runs.
199+
200+
**Step 1:** Generate the timing data by running your full test suite with `--update-shards`:
201+
202+
```bash
203+
./vendor/bin/pest --update-shards
204+
```
205+
206+
This runs all tests and records each test class's duration into `tests/.pest/shards.json`. You can also combine it with `--parallel` to speed things up:
207+
208+
```bash
209+
./vendor/bin/pest --parallel --update-shards
210+
```
211+
212+
**Step 2:** Commit `tests/.pest/shards.json` to your repository. This file is human-readable and looks like this:
213+
214+
```json
215+
{
216+
"timings": {
217+
"Tests\\Feature\\Payments\\StripeCheckoutTest": 1.608,
218+
"Tests\\Feature\\Reports\\SalesReportTest": 2.105,
219+
"Tests\\Unit\\Models\\UserTest": 0.050
220+
},
221+
"checksum": "...",
222+
"updated_at": "2026-04-14T10:30:00+00:00"
223+
}
224+
```
225+
226+
**Step 3:** When you run `--shard` and `tests/.pest/shards.json` exists, Pest automatically uses time-balanced distribution:
227+
228+
```bash
229+
./vendor/bin/pest --shard=1/5
230+
```
231+
232+
The output will indicate that time-balanced sharding is active:
233+
234+
```
235+
Shard: 1 of 5 — 12 files ran, out of 50 (time-balanced).
236+
```
237+
238+
### Keeping Shards Up to Date
239+
240+
When you add or rename test files, Pest will detect that `tests/.pest/shards.json` is out of date. Your tests **will still run** — new test files are distributed evenly across shards, while known tests remain time-balanced. However, Pest will display a warning after the run:
241+
242+
```
243+
WARN The [tests/.pest/shards.json] file is out of date. Run [--update-shards] to update it.
244+
```
245+
246+
Simply re-run `--update-shards` and commit the updated file to restore optimal balancing.
247+
248+
Here is how Pest handles common changes to your test suite:
249+
250+
- **Adding test files**: Tests run with a warning. New files are distributed across shards, known files stay time-balanced.
251+
- **Deleting test files**: Tests run without a warning. Stale timing entries are harmlessly ignored.
252+
- **Adding tests inside an existing file**: Tests run without a warning. The test class is already known — only its internal timing shifts.
253+
- **Renaming a test file**: Tests run with a warning. The old name is ignored, the new name is treated as a new file.
254+
- **Corrupted `shards.json`**: Pest stops with a clear error asking you to delete it or run `--update-shards` to regenerate.
255+
256+
### GitHub Actions Example
257+
258+
Here is a complete example of time-balanced sharding with GitHub Actions:
195259

196260
```yml
197261
strategy:
@@ -202,10 +266,34 @@ name: Tests (Shard ${{ matrix.shard }}/5)
202266

203267
steps:
204268
- name: Run tests
205-
run: pest --parallel --shard ${{ matrix.shard }}/5
269+
run: ./vendor/bin/pest --shard=${{ matrix.shard }}/5
206270
```
207271

208-
This configuration will create five jobs, each running a different shard of your test suite. You can adjust the number of shards based on the size of your test suite and the resources available in your CI environment.
272+
To refresh timing data, you can add a scheduled or manual workflow:
273+
274+
```yml
275+
name: Update Shards
276+
277+
on:
278+
workflow_dispatch:
279+
schedule:
280+
- cron: '0 0 * * 1' # Weekly on Monday
281+
282+
jobs:
283+
update-shards:
284+
runs-on: ubuntu-latest
285+
steps:
286+
- uses: actions/checkout@v4
287+
- name: Update shards.json
288+
run: ./vendor/bin/pest --parallel --update-shards
289+
- name: Commit changes
290+
run: |
291+
git config user.name "github-actions"
292+
git config user.email "github-actions@github.com"
293+
git add tests/.pest/shards.json
294+
git commit -m "chore: update shards.json" || true
295+
git push
296+
```
209297

210298
---
211299

optimizing-tests.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,26 @@ For example, imagine you run your test suite and see the following output:
4949

5050
You can see that the `UserTest > create user` and `OrderTest > create order` tests are taking significantly longer than the other tests. By analyzing this test, you may discover that it's executing several inefficient database queries or performing other expensive operations that could be optimized to reduce its execution time.
5151

52+
## Test Sharding
53+
54+
When running tests in CI, you can split your test suite across multiple jobs using the `--shard` option. Pest supports **time-balanced sharding** — instead of splitting tests evenly by count (which can leave one shard running much longer than others), Pest can distribute tests based on actual execution time.
55+
56+
To enable time-balanced sharding, generate a `tests/.pest/shards.json` file with timing data:
57+
58+
```bash
59+
./vendor/bin/pest --update-shards
60+
```
61+
62+
Then commit `tests/.pest/shards.json` to your repository. When `--shard` is used and this file exists, Pest automatically balances shards by time:
63+
64+
```bash
65+
./vendor/bin/pest --shard=1/4
66+
```
67+
68+
If you add new test files before updating `shards.json`, your tests will still run — new files are distributed evenly across shards while known files remain time-balanced. Pest will display a warning reminding you to run `--update-shards`.
69+
70+
For more details on configuring sharding in CI, including GitHub Actions examples, see [Continuous Integration - Sharding Your Tests](/docs/continuous-integration#sharding-your-tests).
71+
5272
## Compact Printer
5373

5474
If you're working with a large number of tests, it can be beneficial to concentrate solely on the failing tests. You can use the `--compact` printer to instruct Pest to only display test failures, making it easier to pinpoint and resolve any problems without the noise of all of your successful tests.

0 commit comments

Comments
 (0)