Mock Guzzle responses in Symfony tests
Most web applications talk to at least one external service. Maybe you pull in tweets, send email, call a payment provider, or fetch data from an API. If you have functional tests around that code, you want them to be fast and predictable.
I had this problem with the latest-tweet bar on this site. The tweets were fetched with Guzzle, and Guzzle has built-in support for mocked responses. Here is how I used it in Symfony.
Guzzle explains response mocking in its docs. You add Guzzle\Plugin\Mock\MockPlugin as a subscriber to the client object. In Symfony, that client is probably registered as a service, so grab it from the container and add the plugin before running the request in your test:
php<?phpuse Guzzle\Http\Message\Response;use Guzzle\Plugin\Mock\MockPlugin;class HeaderControllerTest extends WebTestCase{public function testTweets(){$client = static::createClient();$twitterClient = $client->getContainer()->get('rvdv_blog.guzzle.twitter_client');$plugin = new MockPlugin();$plugin->addResponse(__DIR__.'/twitter_200_response.txt');$twitterClient->addSubscriber($plugin);$crawler = $client->request('GET', '/_header_tweets');$response = $client->getResponse();$this->assertTrue($response->isSuccessful());$this->assertTrue($response->isCacheable());$this->assertEquals(3600, $response->getMaxAge());$this->assertNotNull($response->getEtag());$this->assertGreaterThan(0, $crawler->filter('li')->count());}}
The client comes from the container, and the mock plugin is added as a subscriber. The text file contains the raw response you would receive from the external service. For example:
bashHTTP/1.1 200 OKDate: Wed, 25 Nov 2009 12:00:00 GMTConnection: closeServer: AmazonS3Content-Type: application/xml<?xml version="1.0" encoding="UTF-8"?><LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/">EU</LocationConstraint>
You can create multiple raw responses for different scenarios. Maybe you want to test a 500 response, a 401 response, or a malformed payload, and then check whether your application handles each case correctly.
To get those raw responses from the service, I used Charles. It is an HTTP proxy that sits between your local environment and the service. Turn Charles on and add these cURL options to your Guzzle client so you can inspect the traffic:
php<?phpuse Guzzle\Http\Client;$client = new Client('https://api.twitter.com/{version}', array('version' => 'v1.1','request.options' => array(// This should be the port you've configured Charles to listen for.'proxy' => 'http://localhost:8888',)));
Once you have captured the responses, remove the proxy configuration and use the files in your tests.