covergenius/phpunit-testlistener-vcr
PHPUnit test listener that records and replays HTTP interactions using a VCR-style approach. Capture real API responses into cassettes during tests, then replay them for fast, deterministic runs without hitting external services.
Installation
composer require --dev covergenius/phpunit-testlistener-vcr
Add the listener to your phpunit.xml:
<listeners>
<listener class="Covergenius\TestListenerVcr\VcrListener" file="./vendor/covergenius/phpunit-testlistener-vcr/src/VcrListener.php" />
</listeners>
Configure PHP-VCR
Ensure php-vcr is installed (php-vcr/php-vcr) and configured in your project. The listener relies on its cassettes.
First Use Case Run tests with VCR recording enabled:
./vendor/bin/phpunit --vcr-record=once
This records HTTP interactions for the first run and replays them in subsequent runs.
Test Execution Lifecycle
--vcr-record=once or --vcr-record=all to capture HTTP calls for new tests.--vcr-record=update to overwrite existing cassettes (e.g., after API changes).Conditional Recording
Leverage PHP-VCR’s shouldIntercept() to exclude specific tests or endpoints:
public function testExternalApi() {
$this->markTestSkipped('Skip VCR for this test');
// OR
VCR::shouldIntercept(function () {
return false; // Disable VCR for this test
});
}
CI/CD Pipeline
--vcr-record=once).--vcr-record=none) for deterministic builds.--vcr-record=update in a post-merge job to sync cassettes.Test Isolation Use unique cassette paths per test class or method to avoid conflicts:
<listeners>
<listener class="Covergenius\TestListenerVcr\VcrListener">
<arguments>
<argument value="tests/_cassettes/%classname%/%methodname%.yaml" type="string"/>
</arguments>
</listener>
</listeners>
Cassette Mismatches
VCR::Error\PlaybackException due to mismatched HTTP responses.--vcr-record=update or manually edit YAML files. Use php-vcr's VCR::shouldIntercept() to debug specific tests.Environment-Specific Responses
dev may fail in staging due to different API responses (e.g., headers, timestamps).VCR::beforeRecord():
VCR::beforeRecord(function ($interaction) {
$interaction->response->headers->remove('date');
});
Listener Initialization Order
Covergenius\TestListenerVcr\VcrListener) and the file attribute in phpunit.xml points to the correct path.Dynamic API Endpoints
/api/v1/users/{id}) may fail replay.VCR::beforeRecord() to rewrite URLs:
VCR::beforeRecord(function ($interaction) {
$interaction->request->uri = str_replace('/api/v1/users/123', '/api/v1/users/{id}', $interaction->request->uri);
});
Verbose Logging Enable debug output to inspect VCR interactions:
./vendor/bin/phpunit --vcr-log-level=debug
Or configure in phpunit.xml:
<listeners>
<listener class="Covergenius\TestListenerVcr\VcrListener">
<arguments>
<argument value="debug" type="string"/>
</arguments>
</listener>
</listeners>
Inspect Cassettes Manually validate YAML cassettes for correctness:
cat tests/_cassettes/MyTest/testMethod.yaml
Use php-vcr's VCR::play() to test replay outside PHPUnit:
VCR::play('tests/_cassettes/MyTest/testMethod.yaml');
Partial Recording
Record only specific tests by combining with PHPUnit’s --filter:
./vendor/bin/phpunit --filter=testExternalApi --vcr-record=once
Custom Listener Logic
Extend the listener by subclassing Covergenius\TestListenerVcr\VcrListener and overriding methods like startTest() or endTest():
class CustomVcrListener extends VcrListener {
public function startTest(Test $test) {
parent::startTest($test);
// Custom logic, e.g., set cassette path dynamically
}
}
Pre/Post-Record Hooks Use PHP-VCR’s hooks to modify interactions:
VCR::beforeRecord(function ($interaction) {
// Modify request/response before recording
});
VCR::afterRecord(function ($interaction) {
// Post-process recorded data
});
Integration with Laravel
AppServiceProvider:
public function register() {
$this->app->make('Covergenius\TestListenerVcr\VcrListener');
}
trait UsesVcr {
protected function enableVcr() {
VCR::configure()->setCassettePath('tests/_cassettes');
}
}
How can I help you explore Laravel packages today?