covergenius/php-vcr
PHP VCR for recording and replaying HTTP interactions during tests. Stores “cassettes” of requests/responses to make suites fast, deterministic, and offline-friendly. Useful for mocking third-party APIs without brittle stubs.
Installation:
composer require --dev covergenius/php-vcr
Add to composer.json under require-dev if not auto-detected.
Basic Configuration:
Create a php-vcr.php config file in your project root (or publish via php artisan vendor:publish --provider="Covergenius\PhpVcr\PhpVcrServiceProvider"):
return [
'cassettes_dir' => base_path('tests/Cassettes'),
'record_mode' => 'once', // or 'new_episodes', 'all'
'default_headers' => [
'Accept' => 'application/json',
],
];
First Use Case:
Wrap a test class or method with @vcr annotation:
use Covergenius\PhpVcr\Annotations\Vcr;
class MyTest extends TestCase
{
/**
* @vcr
*/
public function test_api_endpoint()
{
$response = Http::get('https://api.example.com/data');
$response->assertOk();
}
}
Run tests with:
./vendor/bin/phpunit --vcr
Test Class-Level vs. Method-Level:
/**
* @vcr
*/
class ApiTest extends TestCase { ... }
@vcr on specific tests for granular control.Recording Strategies:
record_mode: 'once': Record once, replay thereafter (default).record_mode: 'new_episodes': Record only if cassette doesn’t exist.record_mode: 'all': Overwrite cassettes on every run (use cautiously).Dynamic Cassettes:
Use {methodName} or {className} placeholders in cassettes_dir:
'cassettes_dir' => base_path('tests/Cassettes/{className}/{methodName}.yaml'),
HTTP Client Integration:
Works seamlessly with Laravel’s Http facade, Guzzle, or Symfony’s Client:
$client = new \GuzzleHttp\Client();
$response = $client->get('https://api.example.com');
Conditional Recording:
Use recordIf() in annotations for dynamic recording logic:
/**
* @vcr(recordIf="app()->environment('local')")
*/
public function test_local_only() { ... }
Cassette Filtering:
Exclude specific tests from VCR via @vcr(skip: true):
/**
* @vcr(skip: true)
*/
public function test_non_recorded() { ... }
Cassette Mismatches:
Cassette mismatch errors.record_mode: 'all' (temporarily).
./vendor/bin/php-vcr update
Environment-Specific Headers:
Authorization) may break across environments.'default_headers' => function () {
return [
'Authorization' => 'Bearer ' . config('services.api.token'),
];
},
Race Conditions:
normalize option:
/**
* @vcr(normalize="['timestamp']")
*/
public function test_with_timestamps() { ... }
Large Cassettes:
ignore:
/**
* @vcr(ignore="['body']")
*/
public function test_with_large_files() { ... }
Parallel Test Runs:
--group in PHPUnit or disable parallelism:
./vendor/bin/phpunit --group=api --vcr
Inspect Cassettes: View raw YAML cassettes for debugging:
cat tests/Cassettes/MyTest/test_api_endpoint.yaml
Verbose Mode: Enable debug output:
./vendor/bin/phpunit --vcr --verbose
Dry Run: Validate cassettes without recording/replaying:
./vendor/bin/php-vcr validate
Custom Matchers:
Extend cassette matching logic by implementing Covergenius\PhpVcr\Matcher\MatcherInterface.
Custom Matchers: Add logic to ignore specific fields (e.g., UUIDs):
PhpVcr::addMatcher(new class implements MatcherInterface {
public function match($expected, $actual) { ... }
});
Pre/Post-Processing: Hook into cassette lifecycle via events:
PhpVcr::listen('cassette.record', function ($cassette) {
$cassette->setMetadata(['recorded_at' => now()]);
});
Remote Cassettes: Store cassettes in S3 or other storage:
'cassettes_dir' => 's3://my-bucket/cassettes',
(Requires custom CassetteStorage implementation.)
CI/CD Integration: Cache cassettes between runs to speed up pipelines:
# .github/workflows/tests.yml
jobs:
test:
steps:
- uses: actions/cache@v3
with:
path: tests/Cassettes
key: ${{ runner.os }}-vcr-${{ hashFiles('**/composer.lock') }}
How can I help you explore Laravel packages today?