Uygulamalar gerçek hayatta gerçek API'leri kullanırlar. Test ortamlarında ise gerçek API'leri kullanıyorlarmış gibi yapıp, sahtelerini kullanırlar, ki zaten bu şekilde olması lazım. Aşağıdaki uygulama posta kodu API'sini kullanıp, adres ile ilgili bilgileri alır. Yapacağımız işlem bu ama biz gerçek olmayan bilgileri alacağız. Taklit işlemi için TestDoubleBundle kullanacağız.


Kurulum


TestDoubleBundle


$ composer require "docteurklein/test-double-bundle":"1.0.0" --dev

AppKernel.php dosyasında aktifleştirmeyi unutmayın. Sadece test içinde kullanın.


public function registerBundles()
{
if (in_array($this->getEnvironment(), ['test'], true)) {
$bundles[] = new DocteurKlein\TestDoubleBundle();
}
}

Parameters.yml


parameters:
....
....
postcodes_api: http://api.postcodes.io/postcodes/

PostcodeService


Bu servis, kullanıcı http://app.dev/app_test.php/postcodes/REAL POSTCODE adresine gittiğinde çalışır.


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;
}
}

Aşağıda gördüğümüz gibi ana servis yerine sadece GuzzleHttp\Client servisini taklit ediyoruz çünkü, API'yi asıl kullanan Guzzle'dir.


services:
app.service.postcode:
class: AppBundle\Service\PostcodeService
arguments:
- "@app.util.guzzle"
- "%postcodes_api%"

app.util.guzzle:
class: GuzzleHttp\Client
tags:
- { name: test_double }

Gerçek sonuç


Bu gerçek hayatta alacağımız sonuçtur.


{
"status": 200,
"result": {
"postcode": "REAL POSTCODE",
"quality": 1,
"eastings": 123456,
"northings": 654321,
"country": "England",
"nhs_ha": "London",
"longitude": -0.87638765373434,
"latitude": 12.33905433211,
"parliamentary_constituency": "Whatever",
"european_electoral_region": "London",
"primary_care_trust": "Hello",
"region": "London",
"lsoa": "Hello 011U",
"msoa": "Hello 066",
"incode": "POSTCODE",
"outcode": "REAL",
"admin_district": "Hello",
"parish": "Hello, nice area",
"admin_county": null,
"admin_ward": "Hello Hill",
"ccg": "NHS Hello",
"nuts": "Hello",
"codes": {
"admin_district": "A0122222",
"admin_county": "B0122222",
"admin_ward": "C0122222",
"parish": "D0122222",
"ccg": "E0122222",
"nuts": "UK890"
}
}
}

Behat.yml


default:
extensions:
Behat\Symfony2Extension: ~
Behat\MinkExtension:
base_url: http://app.dev/app_test.php
sessions:
symfony2:
symfony2: ~
suites:
app:
type: symfony_bundle
bundle: AppBundle
mink_session: symfony2
contexts:
- AppBundle\Features\Context\FeatureContext:
- '%mink.base_url%'

FeatureContext.php


namespace AppBundle\Features\Context;

use Behat\Gherkin\Node\PyStringNode;
use Behat\MinkExtension\Context\MinkContext;
use Behat\Symfony2Extension\Context\KernelAwareContext;
use GuzzleHttp\Psr7\Response;
use InvalidArgumentException;
use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\KernelInterface;

class FeatureContext extends MinkContext implements KernelAwareContext
{
private $baseUri;
/** @var KernelInterface */
private $kernel;
/** @var Crawler */
private $response;

public function __construct($baseUri)
{
$this->baseUri = $baseUri;
}

public function setKernel(KernelInterface $kernel)
{
$this->kernel = $kernel;
}

/**
* @param string $method
* @param string $uri
*
* @When /^I send a "(GET|POST|PUT|PATCH)" request to "([^"]*)"$/
*/
public function iSendRequestTo($method, $uri)
{
/** @var Client $client */
$client = $this->getSession()->getDriver()->getClient();
$this->response = $client->request($method, $this->baseUri.$uri);
}

/**
* @param PyStringNode $stringNode
*
* @Then /^the response should contain json:$/
*/
public function theResponseShouldContainJson(PyStringNode $stringNode)
{
$expectedResponse = json_encode(json_decode($stringNode->getRaw()));

try {
$receivedResponse = json_encode(json_decode($this->response->text()));
if ($expectedResponse != $receivedResponse) {
throw new RuntimeException(sprintf('Response body contains: [%s]', $receivedResponse));
}
} catch (InvalidArgumentException $e) {
if (!$expectedResponse) {
throw new RuntimeException('Response body is empty.');
}
}
}

/**
* @param string $postcode
*
* @Given /^the Postcode API is available for "([^"]*)"$/
*/
public function thePostcodeApiIsAvailableFor($postcode)
{
$postcodesApi = $this->kernel->getContainer()->getParameter('postcodes_api').$postcode;

$response = new Response(200, [], '{"result":{"latitude":0.12345678,"longitude":-0.1234567}}');

$this->kernel
->getContainer()
->get('app.util.guzzle.prophecy')
->request(Request::METHOD_GET, $postcodesApi)
->willReturn($response);
}
}

Postcode.feature


Aşağıda gördüğümüz gibi sahte bir posta kodu kullanıyoruz. Eğer API'yi taklit etmeseydik, bu test gerçek hayatta çalışmazdı.


Feature: Getting postcode details.
In order to get postcode details
As a user
I should be able call call external API

Scenario: I get full result for existent postcode.
Given the Postcode API is available for "DUMMY POSTCODE"
When I send a "GET" request to "/postcodes/DUMMY POSTCODE"
Then the response status code should be 200
And the response should contain json:
"""
{"result":{"latitude":0.12345678,"longitude":-0.1234567}}
"""

Test


$ vendor/bin/behat --suite=app src/AppBundle/Features/Postcode.feature:13
Feature: Getting postcode details.
In order to get postcode details
As a user
I should be able call call external API

Scenario: I get full result for existent postcode.
Given the Postcode API is available for "DUMMY POSTCODE"
When I send a "GET" request to "/postcodes/DUMMY POSTCODE"
Then the response status code should be 200
And the response should contain json:
"""
{"result":{"latitude":0.12345678,"longitude":-0.1234567}}
"""

1 scenario (1 passed)
4 steps (4 passed)
0m0.47s (36.40Mb)