get-skipper/skipper-php

Test-gating via Google Spreadsheet for PHP test frameworks

Maintainers

Package info

github.com/get-skipper/skipper-php

pkg:composer/get-skipper/skipper-php

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v1.0.0 2026-03-15 14:49 UTC

This package is auto-updated.

Last update: 2026-03-15 19:59:47 UTC


README

Tests Latest Version on Packagist PHP License

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

  1. Create a Google Spreadsheet with the following columns in row 1:

    • testId
    • disabledUntil
    • notes (optional)
  2. Create a Google Cloud service account and download the JSON key file.

  3. Share the spreadsheet with the service account's email address (client_email in the JSON).

  4. 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.