Continuous Integration
Up until now, we have only discussed running tests from the command line on your local machine. But, you can also run your tests from a CI platform of your choice. As pestphp/pest is included in your Composer development dependencies, you can easily execute the vendor/bin/pest --ci command within your CI platform's deployment pipeline.
Example With GitHub Actions
If your application uses GitHub Actions as its CI platform, the following guidelines will assist you in configuring Pest so that your application is automatically tested when someone pushes a commit to your GitHub repository.
To get started, create a tests.yml file within the your-project/.github/workflows directory. The file should have the following contents:
1name: Tests 2 3on: ['push', 'pull_request'] 4 5jobs: 6 ci: 7 runs-on: ubuntu-latest 8 9 steps:10 - name: Checkout11 uses: actions/checkout@v312 13 - name: Setup PHP14 uses: shivammathur/setup-php@v215 with:16 php-version: 8.317 tools: composer:v218 coverage: xdebug19 20 - name: Install Dependencies21 run: composer install --no-interaction --prefer-dist --optimize-autoloader22 23 - name: Tests24 run: ./vendor/bin/pest --ci
Naturally, you may customize the script above according to your requirements. For example, you may need to set up a database if your tests require one.
Once you have created your tests.yml file, commit and push the tests.yml file so GitHub Actions can run your tests. Keep in mind that once you make this commit, your test suite will execute on all new pull requests and commits.
Using Browser Testing with GitHub Actions
If you want to use Browser Testing with GitHub Actions, be sure to add a step that installs Playwright before running your tests. Here is an example of how to do this:
1- uses: actions/setup-node@v4 2 with: 3 node-version: lts/* 4 5- name: Install dependencies 6 run: npm ci 7 8- name: Install Playwright Browsers 9 run: npx playwright install --with-deps10 11- name: Run Browser Tests12 run: ./vendor/bin/pest --ci --parallel
Note: Be sure to run your browser tests in parallel to speed up the execution time. You can do this by adding the
--parallelflag to the Pest command.
Example With GitLab CI/CD Pipelines
If your application uses GitLab CI/CD Pipelines as its CI platform, the following guidelines will assist you in configuring Pest so that your application is automatically tested when someone pushes a commit to your GitLab repository.
To get started, add the following configuration to your .gitlab-ci.yml file. The file should have the following contents:
1stages: 2 - build 3 - test 4 5build:vendors: 6 stage: build 7 only: 8 refs: 9 - merge_requests10 - push11 cache:12 key:13 files:14 - composer.lock15 policy: pull-push16 image: composer:217 script:18 - composer install --no-interaction --prefer-dist --optimize-autoloader19 20tests:21 stage: test22 only:23 refs:24 - merge_requests25 - push26 cache:27 key:28 files:29 - composer.lock30 policy: pull31 image: php:8.232 script:33 - ./vendor/bin/pest --ci
Naturally, you may customize the script above according to your requirements. For example, you may need to set up a database if your tests require one.
Once you have created your .gitlab-ci.yml file, commit and push the .gitlab-ci.yml file so Gitlab CI/CD Pipelines can run your tests. Keep in mind that once you make this commit, your test suite will execute on all new merge requests and commits.
Example with Bitbucket Pipelines
If your application uses Bitbucket CI/CD Pipelines as its CI platform, the following guidelines will assist you in configuring Pest so that your application is automatically tested when someone pushes a commit to your Bitbucket repository.
To get started, add the following configuration to your bitbucket-pipelines.yml file. The file should have the following contents:
1image: composer:2 2 3pipelines: 4 default: 5 - parallel: 6 - step: 7 name: Test 8 script: 9 - composer install --no-interaction --prefer-dist --optimize-autoloader10 - ./vendor/bin/pest11 caches:12 - composer
Naturally, you may customize the script above according to your requirements. For example, you may need to set up a database if your tests require one.
Once you have created your bitbucket-pipelines.yml file, commit and push the bitbucket-pipelines.yml file so Bitbucket Pipelines can run your tests. Keep in mind that once you make this commit, your test suite will execute on all new pull requests and commits.
Example with Chipper CI
If your application uses Chipper CI as its CI platform, the following guidelines will assist you in configuring Pest so that your application is automatically tested when someone pushes a commit to your git repository.
To get started, add the following configuration to your .chipperci.yml file. The file should have the following contents:
1version: 1 2 3environment: 4 php: 8.3 5 node: 16 6 7# Optional services 8services: 9# - mysql: 810# - redis:11 12# Build all commits13on:14 push:15 branches: .*16 17pipeline:18 - name: Setup19 cmd: |20 cp -v .env.example .env21 composer install --no-interaction --prefer-dist --optimize-autoloader22 php artisan key:generate23 24 - name: Compile Assets25 cmd: |26 npm ci --no-audit27 npm run build28 29 - name: Test30 cmd: pest
In addition to handling Composer and NPM caches, Chipper CI automatically adds vendor/bin to your PATH, so simply running the pest --ci command will work when running tests.
Naturally, you may customize the scripts above according to your requirements. For example, you may need to define a database service if your tests require one.
Once you have created your .chipperci.yml file, commit and push the .chipperci.yml file so Chipper CI can run your tests. Keep in mind that once you make this commit, your test suite will execute on all new commits.
Sharding Your Tests
If you have a large test suite, you may want to consider sharding your tests across multiple CI jobs to speed up the execution time. Pest supports test sharding out of the box, allowing you to split your tests into smaller groups that can be run in parallel.
To shard your tests, you can use the --shard option when running Pest. For example, if you want to run the first shard of your tests, you can use the following command:
1./vendor/bin/pest --shard=1/5
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.
Time-Balanced Sharding
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.
Step 1: Generate the timing data by running your full test suite with --update-shards:
1./vendor/bin/pest --update-shards
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:
1./vendor/bin/pest --parallel --update-shards
Step 2: Commit tests/.pest/shards.json to your repository. This file is human-readable and looks like this:
1{2 "timings": {3 "Tests\\Feature\\Payments\\StripeCheckoutTest": 1.608,4 "Tests\\Feature\\Reports\\SalesReportTest": 2.105,5 "Tests\\Unit\\Models\\UserTest": 0.0506 },7 "checksum": "...",8 "updated_at": "2026-04-14T10:30:00+00:00"9}
Step 3: When you run --shard and tests/.pest/shards.json exists, Pest automatically uses time-balanced distribution:
1./vendor/bin/pest --shard=1/5
The output will indicate that time-balanced sharding is active:
1Shard: 1 of 5 — 12 files ran, out of 50 (time-balanced).
Keeping Shards Up to Date
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:
1WARN The [tests/.pest/shards.json] file is out of date. Run [--update-shards] to update it.
Simply re-run --update-shards and commit the updated file to restore optimal balancing.
Here is how Pest handles common changes to your test suite:
- Adding test files: Tests run with a warning. New files are distributed across shards, known files stay time-balanced.
- Deleting test files: Tests run without a warning. Stale timing entries are harmlessly ignored.
- Adding tests inside an existing file: Tests run without a warning. The test class is already known — only its internal timing shifts.
- Renaming a test file: Tests run with a warning. The old name is ignored, the new name is treated as a new file.
- Corrupted
shards.json: Pest stops with a clear error asking you to delete it or run--update-shardsto regenerate.
GitHub Actions Example
Here is a complete example of time-balanced sharding with GitHub Actions:
1strategy:2 matrix:3 shard: [1, 2, 3, 4, 5]45name: Tests (Shard ${{ matrix.shard }}/5)67steps:8 - name: Run tests9 run: ./vendor/bin/pest --shard=${{ matrix.shard }}/5
To refresh timing data, you can add a scheduled or manual workflow:
1name: Update Shards 2 3on: 4 workflow_dispatch: 5 schedule: 6 - cron: '0 0 * * 1' # Weekly on Monday 7 8jobs: 9 update-shards:10 runs-on: ubuntu-latest11 steps:12 - uses: actions/checkout@v413 - name: Update shards.json14 run: ./vendor/bin/pest --parallel --update-shards15 - name: Commit changes16 run: |17 git config user.name "github-actions"18 git config user.email "github-actions@github.com"19 git add tests/.pest/shards.json20 git commit -m "chore: update shards.json" || true21 git push
Great job setting up Continuous Integration for your project to ensure codebase stability! Now, let's take a deeper dive into Pest's concepts by exploring it's test configuration capabilities: Configuring Pest →