How to use PHP-VCR to record and replay API calls in PHP

Imen Ezzine
The SensioLabs Tech Blog
5 min readAug 21, 2024
Photo by Josh Chiodo on Unsplash

Integration testing is a bit of a hassle when you are developing PHP apps that interact with external APIs. Making actual API calls can be pricey, depending on whether the server is available and there’s no guarantee of consistent results. In this post, I am going to tell you about a library that I think you’ll find pretty amazing. The best part is that it will save you time and money.

In one of my assignments, I worked on an application that syncs products between a PIM (product information management) and an e-commerce site. Given all that, it was particularly tedious to run integration tests. This is where PHP-VCR comes in. It is a PHP library that lets you record and replay HTTP requests, which makes integration testing a lot easier. In this article, we will look at two ways of using PHP-VCR.

Method 1: PHP-VCR with Static Methods

Step 1: Install PHP-VCR

To get started with PHP-VCR, you will need to install it in your project using Composer. To do this, just run the following command in your terminal:

composer require php-vcr/php-vcr --dev

Step 2: Configure PHP-VCR

Once you have installed it, you’ll need to set up PHP-VCR to log HTTP requests. You can use static methods in PHP-VCR to enable and disable logging, set the capture storage directory, and more.

In one of my earlier articles, I talked about how we used TDD (Test-Driven Development) to build our domain. You can read the article via this link. The idea is to write the tests first and then get into the code. We will take it step by step and write the minimum amount of code.

So, we’ll write our test with Behat, in my case, and once it’s green, we’ll just add the @vcr tag.

If you run the test again, it will create a new file (our cassette) with a record of the request sent and the response.

Step 3: Save and reproduce queries

Behind all the magic is a very simple configuration 😉

Behat lets you use the @BeforeScenario and @AfterScenario annotations to enable and disable recording in the VCR setup. You can use the VCR::turnOn() method to enable recording, then make your HTTP requests as normal.

To get PHP-VCR set up to log HTTP requests in @BeforeScenario, you just need to use the VCR::turnOn() method to enable logging. Then, you can go ahead and make your HTTP requests as usual.

The VCR::insertCassette(‘example’) method records requests and responses on a cassette named ‘example’ in the directory of your choice. Think of the cassette as a record of HTTP interactions.

private bool $vcrOn = false;

/**
* @BeforeScenario
*/
public function setupVCR(BeforeScenarioScope $scenarioEvent): void
{
if ($scenarioEvent->getScenario()->hasTag('vcr')) {
\VCR\VCR::turnOn();
$this->vcrOn = true;
$scenarioCassette = strtolower(str_replace(' ', '_', $scenarioEvent->getScenario()->getTitle()));
$suiteName = $scenarioEvent->getSuite()->getName();
\VCR\VCR::insertCassette($suiteName.'/'.$scenarioCassette.'.yaml');
}
}

Once you’ve made the request and saved the data to the cassette, in the @AfterScenario step, you can use the static VCR::eject() method to stop the request saving. This means that any HTTP interactions will now be read from the cassette instead of making actual HTTP requests.

Finally, we use the static VCR::turnOff() method to turn off HTTP request interception completely. From here on out, PHP-VCR won’t be watching out for outgoing requests.

 /**
* @AfterScenario
*/
public function ejectCassette(): void
{
if ($this->vcrOn) {
\VCR\VCR::eject();
}
\VCR\VCR::turnOff();
}

If you want to take it to the next level, PHP-VCR lets you tweak your settings exactly how you want them:

  • You can adjust the way PHP-VCR matches requests using the configuration option. To see a list of all the matcher names that need to be enabled, just use enableRequestMatchers().
  • PHP-VCR lets you define your own request matchers as callback functions and combine them with existing ones using addRequestMatcher().
  • As I mentioned above, you can save cassettes in the directory of your choice using setCassettePath().
  • PHP-VCR stores HTTP interactions on disk in YAML or JSON format. By default, PHP-VCR uses YAML storage. You can change this using the setStorage() method.
  • The storage mode affects how requests are processed. All we have to do is add the setMode(); method with the desired recording mode. The available modes are:

​ ​ ​​​ ​​ ​​​ ​ ​​ ​ ​​​ ​ ​​ ​ ​​​ ​ ​- once allows new HTTP requests the first time the cassette

​ ​ ​​​ ​​ ​​​ ​ ​​ ​ ​​​ ​ ​​ ​ ​​​ ​ ​ is created, but then throws an exception afterwards.

​ ​ ​​​ ​​ ​​​ ​ ​​ ​ ​​​ ​ ​​ ​ ​​​ ​- none never authorizes new HTTP requests.

​ ​ ​​​ ​​ ​​​ ​ ​​ ​ ​​​ ​ ​​ ​ ​​​ ​- new_episodes always lets new HTTP requests through, which is the default mode.

\VCR\VCR::configure()    
->addRequestMatcher(
'custom_body_matcher',
new CustomBodyMatcher()
)
->enableRequestMatchers(['method', 'url', 'query_string', 'custom_body_matcher', 'post_fields'])
->enableLibraryHooks(['curl'])
->setCassettePath(dirname(__DIR__, 2).'/tests/Environment/IO/cassettes')
->setStorage('yaml')
->setMode('once');

Method 2: PHP-VCR with PHPUnit

Step 1: Install PHP-VCR with PHPUnit

If you want to use PHP-VCR with PHPUnit, you will need to install a few dependencies first:

composer require phpunit/phpunit php-vcr/phpunit-testlistener-vcr --dev

Step 2: Set up PHPUnit

In your PHPUnit configuration file (usually phpunit.xml), just add the PHP-VCR listener and you are all set to use PHP-VCR with PHPUnit.

<phpunit>
<!-- ... other configurations ... -->
<listeners>
<listener class="VCR\PHPUnit\TestListener\VCRTestListener" file="vendor/php-vcr/phpunit-testlistener-vcr/src/VCRTestListener.php" />
</listeners>
</phpunit>

Step 3: Test writing with PHP-VCR

You can now write PHPUnit tests that use PHP-VCR to record and replay HTTP requests. Here’s an example of a test:

class MyApiTest extends TestCase
{
public function setUp(): void
{
// Initialize PHP-VCR before each test
VCR::turnOn();
}

public function tearDown(): void
{
// Disable PHP-VCR after each test
VCR::turnOff();
}

public function testApiCall()
{
// Configure PHP-VCR to log HTTP requests
VCR::insertCassette('testApiCall');

// Your test code that makes an HTTP request to an API
$httpClient = new Client();
$response = $httpClient->get('https://api.example.com/data');

// Assertions on API response
$this->assertEquals(200, $response->getStatusCode());

// Stop PHP-VCR recording
VCR::eject();
}
}

When you run this test, PHP-VCR will record the first HTTP request to the API. When you run the test again, PHP-VCR will use the recorded tape to simulate the API’s response, so you won’t have to worry about making real requests.

Conclusion

PHP-VCR is a powerful tool for making integration testing easier by recording and replaying HTTP requests. No matter if you go with static methods or integrate it with PHPUnit, PHP-VCR can really help you keep your tests stable and consistent by cutting down on your reliance on external services. It’s especially helpful for testing integrations with third-party APIs or web services. If you use it wisely, you can make your tests faster and more reliable.

Find the original article in French here.

--

--