Skip to content

Instantly share code, notes, and snippets.

@tluyben
Last active October 17, 2015 09:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tluyben/538a40f5528c5be82294 to your computer and use it in GitHub Desktop.
Save tluyben/538a40f5528c5be82294 to your computer and use it in GitHub Desktop.
FeatureContext.php for Behat + Laravel 5 testing, fixed/improved from https://github.com/philsturgeon/build-apis-you-wont-hate
<?php
use Behat\Behat\Context\ClosuredContextInterface;
use Behat\Behat\Context\TranslatedContextInterface;
use Behat\Behat\Context\Context;
use Behat\Exception\PendingException;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Illuminate\Routing\UrlGenerator;
use Behat\MinkExtension\Context\MinkContext;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use PHPUnit_Framework_Assert as PHPUnit;
/**
* Features context.
*/
class GuzzleContext extends FeatureContext implements Context
{
/**
* The Guzzle HTTP Client.
*/
protected $client;
/**
* The current resource
*/
protected $resource;
/**
* The request payload
*/
protected $requestPayload;
/**
* The Guzzle HTTP Response.
*/
protected $response;
/**
* The decoded response object.
*/
protected $responsePayload;
/**
* The current scope within the response payload
* which conditions are asserted against.
*/
protected $scope;
/**
* Initializes context.
* Every scenario gets it's own context object.
*
* @param array $parameters context parameters (set them up through behat.yml)
*/
public function __construct()
{
$config = []; #isset($parameters['guzzle']) && is_array($parameters['guzzle']) ? $parameters['guzzle'] : [];
$config['base_uri'] = URL::to('/'); # $parameters['base_url'];
$this->client = new Client($config);
}
/**
* @Given I reseed the database
*/
public function iReseedTheDatabase()
{
if(App::environment() != 'production') {
$path = base_path();
`cd $path; ./artisan db:seed`;
}
}
/**
* @Given /^I have the payload:$/
*/
public function iHaveThePayload(PyStringNode $requestPayload)
{
$this->requestPayload = $requestPayload;
}
/**
* @When /^I request "(GET|PUT|POST|DELETE) ([^"]*)"$/
*/
public function iRequest($httpMethod, $resource)
{
$this->resource = $resource;
$method = strtolower($httpMethod);
try {
switch ($httpMethod) {
case 'PUT':
case 'POST':
$this->response = $this
->client
->$method($resource, null, $this->requestPayload);
break;
default:
$this->response = $this
->client
->$method($resource);
}
} catch (BadResponseException $e) {
$response = $e->getResponse();
// Sometimes the request will fail, at which point we have
// no response at all. Let Guzzle give an error here, it's
// pretty self-explanatory.
if ($response === null) {
throw $e;
}
$this->response = $e->getResponse();
}
}
/**
* @Then /^I get a "([^"]*)" response$/
*/
public function iGetAResponse($statusCode)
{
$response = $this->getResponse();
$contentType = $response->getHeader('Content-Type');
if (is_array($contentType)) {
$contentType = $contentType[0];
}
if ($contentType === 'application/json') {
$bodyOutput = $response->getBody();
} else {
$bodyOutput = 'Output is '.$contentType.', which is not JSON and is therefore scary. Run the request manually.';
}
PHPUnit::assertSame((int) $statusCode, (int) $this->getResponse()->getStatusCode(), $bodyOutput);
}
/**
* @Given /^the "([^"]*)" property equals "([^"]*)"$/
*/
public function thePropertyEquals($property, $expectedValue)
{
$payload = $this->getScopePayload();
$actualValue = $this->arrayGet($payload, $property);
PHPUnit::assertEquals(
$actualValue,
$expectedValue,
"Asserting the [$property] property in current scope equals [$expectedValue]: ".json_encode($payload)
);
}
/**
* @Given /^the "([^"]*)" property exists$/
*/
public function thePropertyExists($property)
{
$payload = $this->getScopePayload();
$message = sprintf(
'Asserting the [%s] property exists in the scope [%s]: %s',
$property,
$this->scope,
json_encode($payload)
);
if (is_object($payload)) {
PHPUnit::assertTrue(array_key_exists($property, get_object_vars($payload)), $message);
} else {
PHPUnit::assertTrue(array_key_exists($property, $payload), $message);
}
}
/**
* @Given /^the "([^"]*)" property is an array$/
*/
public function thePropertyIsAnArray($property)
{
$payload = $this->getScopePayload();
$actualValue = $this->arrayGet($payload, $property);
PHPUnit::assertTrue(
is_array($actualValue),
"Asserting the [$property] property in current scope [{$this->scope}] is an array: ".json_encode($payload)
);
}
/**
* @Given /^the "([^"]*)" property is an object$/
*/
public function thePropertyIsAnObject($property)
{
$payload = $this->getScopePayload();
$actualValue = $this->arrayGet($payload, $property);
PHPUnit::assertTrue(
is_object($actualValue),
"Asserting the [$property] property in current scope [{$this->scope}] is an object: ".json_encode($payload)
);
}
/**
* @Given /^the "([^"]*)" property is an empty array$/
*/
public function thePropertyIsAnEmptyArray($property)
{
$payload = $this->getScopePayload();
$scopePayload = $this->arrayGet($payload, $property);
PHPUnit::assertTrue(
is_array($scopePayload) and $scopePayload === [],
"Asserting the [$property] property in current scope [{$this->scope}] is an empty array: ".json_encode($payload)
);
}
/**
* @Given /^the "([^"]*)" property contains (\d+) items$/
*/
public function thePropertyContainsItems($property, $count)
{
$payload = $this->getScopePayload();
PHPUnit::assertCount(
$count,
$this->arrayGet($payload, $property),
"Asserting the [$property] property contains [$count] items: ".json_encode($payload)
);
}
/**
* @Given /^the "([^"]*)" property is an integer$/
*/
public function thePropertyIsAnInteger($property)
{
$payload = $this->getScopePayload();
PHPUnit::isType(
'int',
$this->arrayGet($payload, $property),
"Asserting the [$property] property in current scope [{$this->scope}] is an integer: ".json_encode($payload)
);
}
/**
* @Given /^the "([^"]*)" property is a string$/
*/
public function thePropertyIsAString($property)
{
$payload = $this->getScopePayload();
PHPUnit::isType(
'string',
$this->arrayGet($payload, $property),
"Asserting the [$property] property in current scope [{$this->scope}] is a string: ".json_encode($payload)
);
}
/**
* @Given /^the "([^"]*)" property is a string equalling "([^"]*)"$/
*/
public function thePropertyIsAStringEqualling($property, $expectedValue)
{
$payload = $this->getScopePayload();
$this->thePropertyIsAString($property);
$actualValue = $this->arrayGet($payload, $property);
PHPUnit::assertSame(
$actualValue,
$expectedValue,
"Asserting the [$property] property in current scope [{$this->scope}] is a string equalling [$expectedValue]."
);
}
/**
* @Given /^the "([^"]*)" property is a boolean$/
*/
public function thePropertyIsABoolean($property)
{
$payload = $this->getScopePayload();
PHPUnit::assertTrue(
gettype($this->arrayGet($payload, $property)) == 'boolean',
"Asserting the [$property] property in current scope [{$this->scope}] is a boolean."
);
}
/**
* @Given /^the "([^"]*)" property is a boolean equalling "([^"]*)"$/
*/
public function thePropertyIsABooleanEqualling($property, $expectedValue)
{
$payload = $this->getScopePayload();
$actualValue = $this->arrayGet($payload, $property);
if (! in_array($expectedValue, ['true', 'false'])) {
throw new \InvalidArgumentException("Testing for booleans must be represented by [true] or [false].");
}
$this->thePropertyIsABoolean($property);
PHPUnit::assertSame(
$actualValue,
$expectedValue == 'true',
"Asserting the [$property] property in current scope [{$this->scope}] is a boolean equalling [$expectedValue]."
);
}
/**
* @Given /^the "([^"]*)" property is a integer equalling "([^"]*)"$/
*/
public function thePropertyIsAIntegerEqualling($property, $expectedValue)
{
$payload = $this->getScopePayload();
$actualValue = $this->arrayGet($payload, $property);
$this->thePropertyIsAnInteger($property);
PHPUnit::assertSame(
$actualValue,
(int) $expectedValue,
"Asserting the [$property] property in current scope [{$this->scope}] is an integer equalling [$expectedValue]."
);
}
/**
* @Given /^the "([^"]*)" property is either:$/
*/
public function thePropertyIsEither($property, PyStringNode $options)
{
$payload = $this->getScopePayload();
$actualValue = $this->arrayGet($payload, $property);
$valid = explode("\n", (string) $options);
PHPUnit::assertTrue(
in_array($actualValue, $valid),
sprintf(
"Asserting the [%s] property in current scope [{$this->scope}] is in array of valid options [%s].",
$property,
implode(', ', $valid)
)
);
}
/**
* @Given /^scope into the first "([^"]*)" property$/
*/
public function scopeIntoTheFirstProperty($scope)
{
$this->scope = "{$scope}.0";
}
/**
* @Given /^scope into the "([^"]*)" property$/
*/
public function scopeIntoTheProperty($scope)
{
$this->scope = $scope;
}
/**
* @Given /^the properties exist:$/
*/
public function thePropertiesExist(PyStringNode $propertiesString)
{
foreach (explode("\n", (string) $propertiesString) as $property) {
$this->thePropertyExists($property);
}
}
/**
* @Given /^reset scope$/
*/
public function resetScope()
{
$this->scope = null;
}
/**
* @Transform /^(\d+)$/
*/
public function castStringToNumber($string)
{
return intval($string);
}
/**
* Checks the response exists and returns it.
*
* @return Guzzle\Http\Message\Response
*/
protected function getResponse()
{
if (! $this->response) {
throw new Exception("You must first make a request to check a response.");
}
return $this->response;
}
/**
* Return the response payload from the current response.
*
* @return mixed
*/
protected function getResponsePayload()
{
if (! $this->responsePayload) {
$json = json_decode($this->getResponse()->getBody(true));
if (json_last_error() !== JSON_ERROR_NONE) {
$message = 'Failed to decode JSON body ';
switch (json_last_error()) {
case JSON_ERROR_DEPTH:
$message .= '(Maximum stack depth exceeded).';
break;
case JSON_ERROR_STATE_MISMATCH:
$message .= '(Underflow or the modes mismatch).';
break;
case JSON_ERROR_CTRL_CHAR:
$message .= '(Unexpected control character found).';
break;
case JSON_ERROR_SYNTAX:
$message .= '(Syntax error, malformed JSON).';
break;
case JSON_ERROR_UTF8:
$message .= '(Malformed UTF-8 characters, possibly incorrectly encoded).';
break;
default:
$message .= '(Unknown error).';
break;
}
throw new Exception($message);
}
$this->responsePayload = $json;
}
return $this->responsePayload;
}
/**
* Returns the payload from the current scope within
* the response.
*
* @return mixed
*/
protected function getScopePayload()
{
$payload = $this->getResponsePayload();
if (! $this->scope) {
return $payload;
}
return $this->arrayGet($payload, $this->scope);
}
/**
* Get an item from an array using "dot" notation.
*
* @copyright Taylor Otwell
* @link http://laravel.com/docs/helpers
* @param array $array
* @param string $key
* @param mixed $default
* @return mixed
*/
protected function arrayGet($array, $key)
{
if (is_null($key)) {
return $array;
}
// if (isset($array[$key])) {
// return $array[$key];
// }
foreach (explode('.', $key) as $segment) {
if (is_object($array)) {
if (! isset($array->{$segment})) {
return;
}
$array = $array->{$segment};
} elseif (is_array($array)) {
if (! array_key_exists($segment, $array)) {
return;
}
$array = $array[$segment];
}
}
return $array;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment