Hello everyone!

We have been investing plenty of personal time and energy for many years to share our knowledge with you all. However, we now need your help to keep this blog running. All you have to do is just click one of the adverts on the site, otherwise it will sadly be taken down due to hosting etc. costs. Thank you.

I'm not going to go into to much details but you can in order to extend the example below. This example includes cURL commands testing as well.


Behat.yml


default:
formatter:
name: pretty
parameters:
output_styles:
comment: [ magenta ]
context:
class: Application\BackendBundle\Features\Context\FeatureContext
parameters:
base_url: 'http://football.local/app_test.php/backend/'
extensions:
Behat\Symfony2Extension\Extension:
mink_driver: true
kernel:
env: test
debug: true
Behat\MinkExtension\Extension:
base_url: 'http://football.local/app_test.php/backend/'
javascript_session: symfony2
goutte: ~
selenium2: ~
paths:
bootstrap: %behat.paths.features%/Context
features: %behat.paths.base%/src/Application/BackendBundle/Features

FeatureContext.php


namespace Site\MainBundle\Features\Context;

use Behat\Behat\Hook\Scope\AfterStepScope;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Mink\Driver\Selenium2Driver;
use Behat\MinkExtension\Context\MinkContext;
use Behat\Symfony2Extension\Context\KernelAwareContext;
use Exception;
use Guzzle\Http\Client;
use SimpleXMLElement;
use Symfony\Component\HttpKernel\KernelInterface;

class FeatureContext extends MinkContext implements KernelAwareContext
{
/**
* Hold Symfony kernel object.
*
* @var object Kernel Object.
*/
protected $kernel;

/**
* Where the failure images will be saved.
*
* @var string Path to save failure screen-shots.
*/
protected $screenShotPath;

/**
* Where the output files will be saved.
*
* @var string Path to save output files.
*/
protected $outputPath;

/**
* Holds base URL.
*
* @var string
*/
protected $baseUrl;

/**
* Holds HTTP client.
*
* @var \Guzzle\Http\Client
*/
protected $client;

/**
* Holds the response.
*
* @var object
*/
protected $response;

/**
* Holds request payload.
*
* @var JSON object
*/
private $requestPayload;

/**
* Holds CLI output.
*
* @var string
*/
protected $cliOutput;

public function __construct(array $parameters)
{
$this->baseUrl = $parameters['base_url'];
$this->client = new Client($parameters['base_url']);
}

/**
* Helps to use doctrine and entity manager.
*
* @param KernelInterface $kernelInterface Interface for getting Kernel.
*/
public function setKernel(KernelInterface $kernelInterface)
{
$this->kernel = $kernelInterface;
}

/**
* @Given /^I have the JSON payload:$/
*
* @param PyStringNode $requestPayload
*/
public function iHaveTheJsonPayload(PyStringNode $requestPayload)
{
$this->requestPayload = $requestPayload;
}

/**
* @Then /^The response body should be:$/
*
* @param PyStringNode $expectedResponseBody
* @throws \Exception
*/
public function theResponseBodyShouldBe(PyStringNode $expectedResponseBody)
{
$responseBody = trim($this->response->getBody(true));

if ($responseBody != $expectedResponseBody) {
throw new Exception(sprintf(
'Expected response body was "%s" but got "%s".',
$expectedResponseBody,
$responseBody
));
}

return;
}

/**
* @When /^I send a "([^"]*)" request to "([^"]*)"$/
*
* @param $method
* @param $uri
*/
public function iSendARequestTo($method, $uri)
{
$request = $this->client->createRequest($method, $uri, null, $this->requestPayload);
$this->response = $this->client->send($request);

//print_r(trim($this->response->getBody(true)));
//print_r($this->response->getContentType()); exit;
}

/**
* @Then /^The response code should be "([^"]*)"$/
*
* @param $responseCode
* @throws \Exception
*/
public function theResponseCodeShouldBe($responseCode)
{
if ($responseCode != $this->response->getStatusCode()) {
throw new Exception(sprintf(
'Expected response was "%s" but got "%s".',
$responseCode,
$this->response->getStatusCode()
));
}

return;
}

/**
* @Then /^The response content type should be "([^"]*)"$/
*
* @param $contentType
* @throws \Exception
*/
public function theResponseContentTypeShouldBe($contentType)
{
if ($this->response->isContentType($contentType) === false) {
throw new Exception(sprintf(
'Expected response was "%s" but got "%s".',
$contentType,
$this->response->getContentType()
));
}

return;
}

/**
* @Then /^The field "([^"]*)" should contain "([^"]*)" in JSON response$/
*
* @param $field
* @param $value
* @throws \Exception
*/
public function theFieldShouldContainInJsonResponse($field, $value)
{
$response = $this->response->json();

if (! is_array($response)) {
throw new Exception('The response is not an array.');
}

if (! isset($response[$field])) {
throw new Exception(sprintf(
'The response array does not contain "%s" key.',
$field
));
}

if ($response[$field] != $value) {
throw new Exception(sprintf(
'The field "%s" does not contain "%s".',
$field,
$value
));
}

return;
}

/**
* @Then /^The property "([^"]*)" should contain "([^"]*)" in XML response$/
*
* @param $property
* @param $value
* @throws \Exception
*/
public function thePropertyShouldContainInXmlResponse($property, $value)
{
$response = $this->response->xml();

if (! $response instanceof SimpleXMLElement) {
throw new Exception('The response is not a SimpleXMLElement object.');
}

if (! property_exists($response, $property)) {
throw new Exception(sprintf(
'The response object does not contain "%s" property.',
$property
));
}

if ($response->$property != $value) {
throw new Exception(sprintf(
'The property "%s" does not contain "%s".',
$property,
$value
));
}

return;
}

/**
* @Then /^The response string should contain "([^"]*)" in HTML response$/
*
* @param $value
* @throws \Exception
*/
public function theResponseStringShouldContainInHtmlResponse($value)
{
$response = trim($this->response->getBody(true));

if (strstr($response, $value) === false) {
throw new Exception(sprintf(
'The response string does not contain "%s".',
$value
));
}

return;
}

/**
* @Then /^The payload field "([^"]*)" should contain "([^"]*)" in JSON response$/
*
* @param $field
* @param $value
* @throws \Exception
*/
public function thePayloadFieldShouldContainInJsonResponse($field, $value)
{
$response = $this->response->json();

if (! is_array($response)) {
throw new Exception('The response is not an array.');
}

if (! isset($response['payload'])) {
throw new Exception('The response array does not contain "payload" key.');
}

if (! isset($response['payload'][$field])) {
throw new Exception(sprintf(
'The response "payload" array does not contain "%s" key.',
$field
));
}

if ($response['payload'][$field] != $value) {
throw new Exception(sprintf(
'The field "%s" does not contain "%s".',
$field,
$value
));
}

return;
}

/**
* @Then /^The payload property "([^"]*)" should contain "([^"]*)" in XML response$/
*
* @param $property
* @param $value
* @throws \Exception
*/
public function thePayloadPropertyShouldContainInXmlResponse($property, $value)
{
$response = $this->response->xml();

$payload = 'payload';

if (! $response instanceof SimpleXMLElement) {
throw new Exception('The response is not a SimpleXMLElement object.');
}

if (! property_exists($response, 'payload')) {
throw new Exception('The response object does not contain "payload" property.');
}

if (! $response->$payload instanceof SimpleXMLElement) {
throw new Exception('The property "payload" is not a SimpleXMLElement object.');
}

if (! property_exists($response->$payload, $property)) {
throw new Exception(sprintf(
'The response object in "payload" property does not contain "%s" property.',
$property
));
}

if ($response->$payload->$property != $value) {
throw new Exception(sprintf(
'The property "%s" does not contain "%s".',
$property,
$value
));
}

return;
}

/**
* @When /^I run curl command: "([^"]*)"$/
*
* @param $command
*/
public function iRunCurlCommand($command)
{
$command = str_replace('VAR1', '"application/json"', $command);
$command = str_replace('VAR2', $this->requestPayload, $command);
$command = str_replace('VAR3', $this->baseUrl, $command);

exec($command, $output);
$this->cliOutput = trim(implode("\n", $output));
}

/**
* @Then /^I should get:$/
*
* @param PyStringNode $string
* @throws \Exception
*/
public function iShouldGet(PyStringNode $string)
{
if ((string) $string !== trim($this->cliOutput)) {
throw new Exception(sprintf(
'Expected "%s" but received "%s".',
$string,
$this->cliOutput
));
}

return;
}
}

CLI feature


Feature: Post Rest Cli Call
In order to test Rest API with direct CLI call
As a user
I should be able to call API URL

#JSON
Scenario: Rest POST API - JSON response
Given I have the JSON payload:
"""
{
"token": "ThisTokenIsNotSoSecretChangeIt",
"name": "Micky",
"surname": "Mouse"
}
"""
When I run curl command: "curl -v -X POST -H VAR1 -d 'VAR2' VAR3/api/v1/user.json"
Then I should get:
"""
{
"response": "success",
"code": 202,
"message": "OK!",
"payload": {
"token": "ThisTokenIsNotSoSecretChangeIt",
"name": "Micky",
"surname": "Mouse"
}
}
"""

#XML
Scenario: Rest POST API - XML response
Given I have the JSON payload:
"""
{
"token": "ThisTokenIsNotSoSecretChangeIt",
"name": "Micky",
"surname": "Mouse"
}
"""
When I run curl command: "curl -v -X POST -H VAR1 -d 'VAR2' VAR3/api/v1/user.xml"
Then I should get:
"""
<?xml version="1.0" encoding="UTF-8"?>
<user>
<response>success</response>
<code>202</code>
<message>OK!</message>
<payload>
<token>ThisTokenIsNotSoSecretChangeIt</token>
<name>Micky</name>
<surname>Mouse</surname>
</payload>
</user>
"""

#HTML
Scenario: Rest POST API - HTML response
Given I have the JSON payload:
"""
{
"token": "ThisTokenIsNotSoSecretChangeIt",
"name": "Micky",
"surname": "Mouse"
}
"""
When I run curl command: "curl -v -X POST -H VAR1 -d 'VAR2' VAR3/api/v1/user.html"
Then I should get:
"""
success 202 OK! ThisTokenIsNotSoSecretChangeIt Micky Mouse
"""

REST feature


Feature: Post Rest Url Call
In order to test Rest API with direct URL call
As a user
I should be able to call API URL

#JSON
Scenario: Rest POST API - JSON response
Given I have the JSON payload:
"""
{
"token": "ThisTokenIsNotSoSecretChangeIt",
"name": "Micky",
"surname": "Mouse"
}
"""
When I send a "POST" request to "api/v1/user.json"
Then The response code should be "202"
And The response content type should be "application/json"
And The response body should be:
"""
{
"response": "success",
"code": 202,
"message": "OK!",
"payload": {
"token": "ThisTokenIsNotSoSecretChangeIt",
"name": "Micky",
"surname": "Mouse"
}
}
"""
And The field "response" should contain "success" in JSON response
And The field "code" should contain "202" in JSON response
And The field "message" should contain "OK!" in JSON response
And The payload field "token" should contain "ThisTokenIsNotSoSecretChangeIt" in JSON response
And The payload field "name" should contain "Micky" in JSON response
And The payload field "surname" should contain "Mouse" in JSON response

#XML
Scenario: Rest POST API - XML response
Given I have the JSON payload:
"""
{
"token": "ThisTokenIsNotSoSecretChangeIt",
"name": "Micky",
"surname": "Mouse"
}
"""
When I send a "POST" request to "api/v1/user.xml"
Then The response code should be "202"
And The response content type should be "application/xml"
And The response body should be:
"""
<?xml version="1.0" encoding="UTF-8"?>
<user>
<response>success</response>
<code>202</code>
<message>OK!</message>
<payload>
<token>ThisTokenIsNotSoSecretChangeIt</token>
<name>Micky</name>
<surname>Mouse</surname>
</payload>
</user>
"""
And The property "response" should contain "success" in XML response
And The property "code" should contain "202" in XML response
And The property "message" should contain "OK!" in XML response
And The payload property "token" should contain "ThisTokenIsNotSoSecretChangeIt" in XML response
And The payload property "name" should contain "Micky" in XML response
And The payload property "surname" should contain "Mouse" in XML response

#HTML
Scenario: Rest POST API - HTML response
Given I have the JSON payload:
"""
{
"token": "ThisTokenIsNotSoSecretChangeIt",
"name": "Micky",
"surname": "Mouse"
}
"""
When I send a "POST" request to "api/v1/user.html"
Then The response code should be "202"
And The response content type should be "text/html"
And The response body should be:
"""
success 202 OK! ThisTokenIsNotSoSecretChangeIt Micky Mouse
"""
And The response string should contain "success" in HTML response
And The response string should contain "202" in HTML response
And The response string should contain "OK!" in HTML response
And The response string should contain "ThisTokenIsNotSoSecretChangeIt" in HTML response
And The response string should contain "Micky" in HTML response
And The response string should contain "Mouse" in HTML response

HTML feature


# This is not an ideal way of doing Rest testing because response is not outputted into browser in real life.
# Runs with selenium2 driver in behat.yml otherwise fails.

Feature: Post Rest Browser
In order to test Rest API with browser
As a user
I should be able use web form for POST

#JSON
Scenario: Rest POST API - JSON response
Given I am on "/"
Then I should see "USER API"

When I follow "CREATE"
Then I should see "CREATE"
And I fill in "user_create_name" with "Roger"
And I fill in "user_create_surname" with "Rabbit"
And I select "json" from "user_create_response"
And I press "Create"
Then I should see "HTTP/1.1 202 Accepted"
And I should see "Content-Type: application/json"
And I should see "{ \"response\": \"success\", \"code\": 202, \"message\": \"OK!\", \"payload\": { \"token\": \"ThisTokenIsNotSoSecretChangeIt\", \"name\": \"Roger\", \"surname\": \"Rabbit\" } }"

#XML
Scenario: Rest POST API - JSON response
Given I am on "/"
Then I should see "USER API"

When I follow "CREATE"
Then I should see "CREATE"
And I fill in "user_create_name" with "Roger"
And I fill in "user_create_surname" with "Rabbit"
And I select "xml" from "user_create_response"
And I press "Create"
Then I should see "HTTP/1.1 202 Accepted"
And I should see "Content-Type: application/xml"
And I should see "success 202 OK!"

#HTML
Scenario: Rest POST API - JSON response
Given I am on "/"
Then I should see "USER API"

When I follow "CREATE"
Then I should see "CREATE"
And I fill in "user_create_name" with "Roger"
And I fill in "user_create_surname" with "Rabbit"
And I select "html" from "user_create_response"
And I press "Create"
Then I should see "HTTP/1.1 202 Accepted"
And I should see "Content-Type: text/html"
And I should see "success 202 OK!"

References


For more examples, you can check links below.