In real life we call real APIs to get data but in the case of tests we shouldn't so the example below will pretend like we're calling real API.


PostcodeService


namespace AppBundle\Service;

use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Stream;
use Symfony\Component\HttpFoundation\Request;

class PostcodeService
{
private $client;
private $apiUri;

public function __construct(
ClientInterface $client,
$apiUri
) {
$this->client = $client;
$this->apiUri = $apiUri;
}

public function get($postcode)
{
return $this->getDetails($postcode);
}

private function getDetails($postcode)
{
$details = null;

try {
/** @var Response $response */
$response = $this->client->request(Request::METHOD_GET, $this->apiUri.$postcode);
/** @var Stream $body */
$body = $response->getBody();
$details = $body->getContents();
} catch (ClientException $e) {
}

return $details;
}
}

services:
app.service.postcode:
class: AppBundle\Service\PostcodeService
arguments:
- "@app.util.guzzle"
- "http://api.postcodes.io/postcodes/" # This would normally be in parameters.yml

app.util.guzzle:
class: GuzzleHttp\Client

PostcodeServiceSpec


namespace spec\AppBundle\Service;

use GuzzleHttp\ClientInterface;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Stream;
use PhpSpec\ObjectBehavior;
use Symfony\Component\HttpFoundation\Request;

class PostcodeServiceSpec extends ObjectBehavior
{
// Real one: http://api.postcodes.io/postcodes/
private $apiUri = 'http://mock.postcodes.io/postcodes/';

function let(
ClientInterface $client
) {
$this->beConstructedWith(
$client,
$this->apiUri
);
}

function it_should_return_empty_details_for_non_existent_postcode(
ClientInterface $client,
Response $response,
Stream $body
) {
$postcode = 'NON-EXISTENT';
$result = null;

$client->request(Request::METHOD_GET, $this->apiUri.$postcode)->shouldBeCalled()->willReturn($response);
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($result);

$this->get($postcode)->shouldReturn($result);
}

function it_should_return_actual_details_for_existent_postcode(
ClientInterface $client,
Response $response,
Stream $body
) {
$postcode = 'EXISTENT';
$result = '{"status":200,"result":{"postcode":"'.$postcode.'"}}';

$client->request(Request::METHOD_GET, $this->apiUri.$postcode)->shouldBeCalled()->willReturn($response);
$response->getBody()->shouldBeCalled()->willReturn($body);
$body->getContents()->shouldBeCalled()->willReturn($result);

$this->get($postcode)->shouldReturn($result);
}
}