PHPackages                             vielhuber/cassette - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. vielhuber/cassette

ActiveLibrary

vielhuber/cassette
==================

Record real HTTP flows once, replay them deterministically as regression tests.

1.1.3(1mo ago)15↑2900%MITPHPPHP &gt;=8.3

Since Mar 27Pushed 1mo agoCompare

[ Source](https://github.com/vielhuber/cassette)[ Packagist](https://packagist.org/packages/vielhuber/cassette)[ RSS](/packages/vielhuber-cassette/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (1)Versions (15)Used By (0)

📼 cassette 📼
============

[](#-cassette-)

cassette hooks into your PHP application at the lowest level — intercepting database calls and outgoing HTTP requests via [uopz](https://www.php.net/manual/en/book.uopz.php) and serialises every return value to a JSON tape. On replay, the server runs normally but all external I/O is served from that tape, making each test completely self-contained: no database, no network, no side effects. Visual regression is layered on top via Playwright, which navigates through the recorded request sequence and compares screenshots against committed baselines.

Installation
------------

[](#installation)

```
composer require --dev vielhuber/cassette
```

**Requirements:**

- PHP ≥ 8.3 with the [uopz](https://www.php.net/manual/en/book.uopz.php) extension
- Node.js (for visual screenshot regression — installed automatically on first use)

**Bootstrap** — add one line to your entry point (e.g. `wp-config.php`) before any application code runs:

```
require_once __DIR__ . '/vendor/vielhuber/cassette/src/bootstrap.php';
```

The bootstrap is a no-op when no cassette is active, so it is always safe to keep this line in place.

`.cassette/config.json` is created automatically on the first `vendor/bin/cassette record` call.

Usage
-----

[](#usage)

CommandDescription`vendor/bin/cassette record `Switch to record mode and clear old data — then click through the flow in the browser`vendor/bin/cassette stop `Stop recording — further requests are no longer captured`vendor/bin/cassette replay  [--refresh] [--base-url=] [--http-only|--screenshot-only]`HTTP diff + visual screenshot comparison; `--refresh` recreates baselines`vendor/bin/cassette accept `Interactively accept HTTP diffs as new baseline`vendor/bin/cassette delete `Delete a run including all its data and screenshots`vendor/bin/cassette delete --all`Delete all runs`vendor/bin/cassette list`List all recorded runs with request count and screenshotsExit code `0` = all green, `1` = deviations found. CI-compatible.

All run data is stored in `.cassette/runs//`. Screenshot baselines are stored in `.cassette/runs//screenshots/` and should be committed to git.

If runs should **not** be tracked in git at all, add the entire directory to `.gitignore`:

```
/.cassette/

```

Development workflow (working on cassette itself)
-------------------------------------------------

[](#development-workflow-working-on-cassette-itself)

When developing cassette alongside a project, you can run the CLI directly from the cassette source directory and point it at the target project via `--root`:

```
cd /var/www/cassette
./cassette record run_001 --root=/var/www/my-project
./cassette stop   run_001 --root=/var/www/my-project
./cassette replay run_001 --root=/var/www/my-project
```

This way the target project's `composer.json` stays completely untouched — no path-repository, no `minimum-stability`, no symlinks. All cassette data is read from and written to `/var/www/my-project/.cassette/` as usual.

Portability
-----------

[](#portability)

Recordings are captured on one host (e.g. `https://custom-tld.dev`) but can be replayed anywhere — CI, localhost, staging — without re-recording:

```
# Replay against a different host than the one used during recording
vendor/bin/cassette replay run_001 --base-url=http://localhost
```

The `--base-url` flag replaces the host for both the HTTP diff and the Playwright screenshots. The recorded `base_url` in `http.json` is never modified.

For GitHub Actions, pass the URL as a secret:

```
- name: Replay cassettes
  run: vendor/bin/cassette replay run_001 --base-url=${{ secrets.APP_URL }}
```

### No database required for replay

[](#no-database-required-for-replay)

During replay, all database queries and outgoing HTTP calls are intercepted by [uopz](https://www.php.net/manual/en/book.uopz.php) and served from the recorded cassette data. The server must be running and reachable, but:

- **no real database connection is needed** — the DB state at replay time is completely irrelevant
- **no test fixtures or seed data** are required
- external APIs and curl calls are mocked the same way

This makes cassette replays safe to run on CI, on a fresh machine, or against a server whose database is empty, stale, or even offline.

### curl interception

[](#curl-interception)

To intercept `__::curl()` calls, add [`vielhuber/stringhelper`](https://github.com/vielhuber/stringhelper) to your project:

```
composer require vielhuber/stringhelper
```

Without it, cassette still records and replays all database calls — curl interception is simply skipped.

Configuration
-------------

[](#configuration)

Create `.cassette/config.json` to customise screenshot behaviour per project:

```
{
    "screenshot": {
        "headless": true,
        "zoom": 0.7,
        "maxDiffPixelRatio": 0.01,
        "maskSelectors": [".footer__meta"]
    }
}
```

KeyDefaultDescription`screenshot.headless``true`Run Playwright in headless mode`screenshot.zoom``0.7`CSS zoom applied to `` before each screenshot`screenshot.maxDiffPixelRatio``0.01`Maximum allowed pixel difference ratio (0–1)`screenshot.maskSelectors``[]`CSS selectors whose elements are hidden before each screenshot

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance90

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

14

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/d959b840f9893a14dfc78f65588f427d5bf606b885616e95dbe5a11199a8fc21?d=identicon)[vielhuber](/maintainers/vielhuber)

---

Top Contributors

[![vielhuber](https://avatars.githubusercontent.com/u/3183737?v=4)](https://github.com/vielhuber "vielhuber (14 commits)")

### Embed Badge

![Health badge](/badges/vielhuber-cassette/health.svg)

```
[![Health](https://phpackages.com/badges/vielhuber-cassette/health.svg)](https://phpackages.com/packages/vielhuber-cassette)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
