get-skipper / skipper-php
Test-gating via Google Spreadsheet for PHP test frameworks
Requires
- php: >=8.2
- google/apiclient: ^2.15
Requires (Dev)
- behat/behat: ^3.29
- codeception/codeception: ^5.3
- friendsofphp/php-cs-fixer: ^3.0
- kahlan/kahlan: ^6.1
- pestphp/pest: ^2.0
- phpspec/phpspec: ^7 || ^8
- phpunit/phpunit: ^10
Suggests
- behat/behat: Required to use GetSkipper\Behat integration (^3.12)
- codeception/codeception: Required to use GetSkipper\Codeception integration (^5.0)
- kahlan/kahlan: Required to use GetSkipper\Kahlan integration (^5.0)
- pestphp/pest: Required to use GetSkipper\Pest integration (^2 || ^3)
- phpspec/phpspec: Required to use GetSkipper\PHPSpec integration (^7 || ^8)
- phpunit/phpunit: Required to use GetSkipper\PHPUnit integration (^10 || ^11 || ^12)
This package is auto-updated.
Last update: 2026-03-15 19:59:47 UTC
README
Test-gating for PHP via Google Spreadsheet. Enable or disable tests without changing code — just update a date in a Google Sheet.
A PHP port of get-skipper/skipper, supporting PHPUnit, Pest, Behat, Codeception, PHPSpec, and Kahlan.
How it works
A Google Spreadsheet stores test IDs with optional disabledUntil dates:
| testId | disabledUntil | notes |
|---|---|---|
tests/Feature/AuthTest.php > AuthTest > testItCanLogin |
||
tests/Feature/PaymentTest.php > PaymentTest > testCheckout |
2099-12-31 |
Flaky on CI |
features/auth.feature > Auth > User can log in |
2026-06-01 |
Under investigation |
- Empty
disabledUntil→ test runs normally - Past date → test runs normally
- Future date → test is skipped automatically
Tests not listed in the spreadsheet always run (opt-out model).
Installation
composer require get-skipper/skipper-php
Install your test framework if not already present:
# PHPUnit / Pest composer require --dev phpunit/phpunit # Behat composer require --dev behat/behat # Codeception composer require --dev codeception/codeception # PHPSpec composer require --dev phpspec/phpspec # Kahlan composer require --dev kahlan/kahlan
Google Sheets setup
-
Create a Google Spreadsheet with the following columns in row 1:
testIddisabledUntilnotes(optional)
-
Create a Google Cloud service account and download the JSON key file.
-
Share the spreadsheet with the service account's email address (
client_emailin the JSON). -
Note the spreadsheet ID from the URL:
https://docs.google.com/spreadsheets/d/YOUR_SPREADSHEET_ID/edit
Credentials
Three formats are accepted for all integrations:
| Format | Parameter | Use case |
|---|---|---|
| File path | credentialsFile: './service-account.json' |
Local development |
| Base64 string | credentialsBase64: 'eyJ0eX...' |
CI/CD inline secret |
| Environment variable | credentialsEnvVar: 'GOOGLE_CREDS_B64' |
CI/CD env var (base64) |
To encode your credentials file for CI:
base64 -i service-account.json
Framework integrations
PHPUnit (10 / 11 / 12)
Add to phpunit.xml:
<?xml version="1.0" encoding="UTF-8"?> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="Tests"> <directory>tests</directory> </testsuite> </testsuites> <extensions> <bootstrap class="GetSkipper\PHPUnit\SkipperExtension"> <parameter name="spreadsheetId" value="YOUR_SPREADSHEET_ID"/> <!-- Choose one credentials option: --> <parameter name="credentialsFile" value="./service-account.json"/> <!-- <parameter name="credentialsBase64" value="eyJ0eXBlIjoic2VydmljZV9hY2NvdW50Ii4uLn0="/> --> <!-- <parameter name="credentialsEnvVar" value="GOOGLE_CREDS_B64"/> --> <parameter name="sheetName" value="MySheet"/> <!-- optional --> </bootstrap> </extensions> </phpunit>
Test ID format:
tests/Unit/AuthTest.php > AuthTest > testItCanLogin
tests/Unit/AuthTest.php > AuthTest > testWithDataProvider with data set "valid"
Pest (v2 / v3)
Pest runs on top of PHPUnit — use the same phpunit.xml configuration above. The extension auto-detects Pest-generated classes (P\ namespace prefix) and applies the correct ID format automatically.
Test ID format:
tests/Feature/auth.php > can login
tests/Feature/auth.php > Auth > can login ← with describe() block
Alternative: hook-based setup via tests/Pest.php
Use this if you prefer Pest-native configuration instead of phpunit.xml:
<?php // tests/Pest.php use GetSkipper\Core\Config\SkipperConfig; use GetSkipper\Core\Credentials\Base64Credentials; use GetSkipper\Core\Credentials\FileCredentials; use GetSkipper\Pest\Plugin; // Choose one credentials option: Plugin::skipperSetup(new SkipperConfig( spreadsheetId: 'YOUR_SPREADSHEET_ID', credentials: new FileCredentials('./service-account.json'), // credentials: new Base64Credentials('eyJ0eXBlIjoic2VydmljZV9hY2NvdW50Ii4uLn0='), // credentials: new Base64Credentials((string) getenv('GOOGLE_CREDS_B64')), sheetName: 'MySheet', // optional ));
Behat (3.x)
Add to behat.yml:
default: extensions: GetSkipper\Behat\SkipperExtension: spreadsheetId: 'YOUR_SPREADSHEET_ID' # Choose one credentials option: credentialsFile: './service-account.json' # credentialsBase64: 'eyJ0eXBlIjoic2VydmljZV9hY2NvdW50Ii4uLn0=' # credentialsEnvVar: 'GOOGLE_CREDS_B64' sheetName: 'MySheet' # optional suites: default: contexts: - GetSkipper\Behat\SkipperContext # Add your other contexts here - App\Context\FeatureContext
Disabled scenarios are marked as Pending (yellow).
Test ID format:
features/auth/login.feature > User authentication > User can log in
Codeception (5.x)
Add to codeception.yml:
extensions: enabled: - GetSkipper\Codeception\SkipperExtension config: GetSkipper\Codeception\SkipperExtension: spreadsheetId: 'YOUR_SPREADSHEET_ID' # Choose one credentials option: credentialsFile: './service-account.json' # credentialsBase64: 'eyJ0eXBlIjoic2VydmljZV9hY2NvdW50Ii4uLn0=' # credentialsEnvVar: 'GOOGLE_CREDS_B64' sheetName: 'MySheet' # optional
Test ID format:
tests/Acceptance/AuthCest.php > AuthCest > tryToLogin
tests/Unit/AuthTest.php > AuthTest > testItCanLogin
PHPSpec (7 / 8)
Add to phpspec.yml:
extensions: GetSkipper\PHPSpec\SkipperExtension: spreadsheetId: 'YOUR_SPREADSHEET_ID' # Choose one credentials option: credentialsFile: './service-account.json' # credentialsBase64: 'eyJ0eXBlIjoic2VydmljZV9hY2NvdW50Ii4uLn0=' # credentialsEnvVar: 'GOOGLE_CREDS_B64' sheetName: 'MySheet' # optional
Disabled specs are marked as Skipped.
Test ID format:
spec/Auth/LoginSpec.php > LoginSpec > it login with valid credentials
spec/Auth/LoginSpec.php > LoginSpec > it reject invalid passwords
Kahlan (5.x / 6.x)
In kahlan-config.php:
<?php // kahlan-config.php use GetSkipper\Core\Config\SkipperConfig; use GetSkipper\Core\Credentials\Base64Credentials; use GetSkipper\Core\Credentials\FileCredentials; use GetSkipper\Kahlan\SkipperPlugin; $config->beforeAll(function () { // Choose one credentials option: SkipperPlugin::setup(new SkipperConfig( spreadsheetId: 'YOUR_SPREADSHEET_ID', credentials: new FileCredentials('./service-account.json'), // credentials: new Base64Credentials('eyJ0eXBlIjoic2VydmljZV9hY2NvdW50Ii4uLn0='), // credentials: new Base64Credentials((string) getenv('GOOGLE_CREDS_B64')), sheetName: 'MySheet', // optional )); }); // Check tests globally (all specs in all directories): $config->scope()->beforeEach(function () { SkipperPlugin::checkTest($this); });
Test ID format:
spec/Auth/LoginSpec.php > Auth > Login > can login with valid credentials
Sync mode
In sync mode, the spreadsheet is automatically reconciled with your test suite:
- New tests are added as rows (with empty
disabledUntil) - Removed tests are deleted from the spreadsheet
Enable with the SKIPPER_MODE environment variable:
# PHPUnit / Pest SKIPPER_MODE=sync vendor/bin/phpunit # Behat SKIPPER_MODE=sync vendor/bin/behat # Codeception SKIPPER_MODE=sync vendor/bin/codecept run # PHPSpec SKIPPER_MODE=sync vendor/bin/phpspec run # Kahlan SKIPPER_MODE=sync vendor/bin/kahlan
Note: Sync only writes to the primary sheet. Reference sheets are never modified.
Sync via GitHub Actions
The bundled workflow (.github/workflows/tests.yml) includes a sync job that runs automatically after every push to main, once all tests have passed:
sync: name: Sync spreadsheet needs: tests if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: - run: composer test env: SKIPPER_MODE: sync GOOGLE_CREDS_B64: ${{ secrets.GOOGLE_CREDS_B64 }}
This ensures the spreadsheet is always up to date with the current test suite on main. The sync job is skipped on pull requests and on branches other than main.
Reference sheets
You can merge test entries from multiple sheets. When the same test ID appears in multiple sheets, the most restrictive (latest) disabledUntil wins.
Configure additional sheets with referenceSheets:
<!-- phpunit.xml --> <parameter name="sheetName" value="Main"/> <parameter name="referenceSheets" value='["SharedDisabled", "QA"]'/>
# behat.yml / phpspec.yml / codeception.yml sheetName: 'Main' referenceSheets: ['SharedDisabled', 'QA']
Environment variables
| Variable | Default | Description |
|---|---|---|
SKIPPER_MODE |
read-only |
Set to sync to enable spreadsheet reconciliation |
SKIPPER_CACHE_FILE |
(auto) | Path to the resolver cache file (set by the main process) |
SKIPPER_DISCOVERED_DIR |
(auto) | Directory for collecting discovered test IDs across workers |
SKIPPER_DEBUG |
(unset) | Set to any non-empty value to enable verbose logging |
Test ID format reference
| Framework | Format example |
|---|---|
| PHPUnit | tests/Unit/AuthTest.php > AuthTest > testItCanLogin |
| Pest | tests/Feature/auth.php > Auth > can login |
| Behat | features/auth.feature > User authentication > User can log in |
| Codeception | tests/Acceptance/AuthCest.php > AuthCest > tryToLogin |
| PHPSpec | spec/Auth/LoginSpec.php > LoginSpec > it login with valid credentials |
| Kahlan | spec/Auth/LoginSpec.php > Auth > Login > can login |
All test IDs are case-insensitive and whitespace-collapsed for comparison.