Skip to content

Instantly share code, notes, and snippets.

@weaverryan
Last active September 23, 2019 15:22
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save weaverryan/4e82d54f15cb104a743e to your computer and use it in GitHub Desktop.
Save weaverryan/4e82d54f15cb104a743e to your computer and use it in GitHub Desktop.
Example Symfony ApiTestCase (from WIP KnpUniversity Symfony REST tutorial)
<?php
namespace AppBundle\Tests\Controller\Api;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\EntityManager;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Message\ResponseInterface;
use GuzzleHttp\Subscriber\History;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\PropertyAccess\Exception\RuntimeException;
use Symfony\Component\PropertyAccess\Exception\AccessException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
class ApiTestCase extends KernelTestCase
{
private static $staticClient;
/**
* @var History
*/
private static $history;
/**
* @var Client
*/
protected $client;
private $output;
/**
* @var PropertyAccessor
*/
private $accessor;
public static function setUpBeforeClass()
{
self::$staticClient = new Client([
'base_url' => 'http://localhost:9003',
'defaults' => [
'exceptions' => false
]
]);
self::$history = new History();
self::$staticClient->getEmitter()
->attach(self::$history);
self::bootKernel();
}
protected function setUp()
{
$this->client = self::$staticClient;
$this->purgeDatabase();
}
/**
* Clean up Kernel usage in this test.
*/
protected function tearDown()
{
// purposefully not calling parent class, which shuts down the kernel
}
/**
* Automatically prints the last response on a failure
*/
protected function onNotSuccessfulTest(Exception $e)
{
if (self::$history && $lastResponse = self::$history->getLastResponse()) {
$lastRequest = self::$history->getLastRequest();
$this->printDebug('');
$this->printDebug('<error>Failure!</error> when making the following request:');
$this->printDebug(sprintf('<comment>%s</comment>: <info>%s</info>', $lastRequest->getMethod(), $lastRequest->getUrl())."\n");
$this->debugResponse($lastResponse);
}
throw $e;
}
private function purgeDatabase()
{
$purger = new ORMPurger($this->getService('doctrine.orm.default_entity_manager'));
$purger->purge();
}
protected function getService($id)
{
return self::$kernel->getContainer()
->get($id);
}
/**
* Prints the given response to the screen in a nice debug way
*/
protected function debugResponse(ResponseInterface $response)
{
$body = (string) $response->getBody();
$contentType = $response->getHeader('Content-Type');
if ($contentType == 'application/json' || strpos($contentType, '+json') !== false) {
$data = json_decode($body);
if ($data === null) {
// invalid JSON!
$this->printDebug($body);
} else {
// valid JSON, print it pretty
$this->printDebug(json_encode($data, JSON_PRETTY_PRINT));
}
} else {
// the response is HTML - see if we should print all of it or some of it
$isValidHtml = strpos($body, '</body>') !== false;
if ($isValidHtml) {
$this->printDebug('<error>Failure!</error> Below is a summary of the HTML response from the server.');
// finds the h1 and h2 tags and prints them only
$crawler = new Crawler($body);
$i = 1;
foreach ($crawler->filter('h1, h2')->extract(array('_text')) as $header) {
$this->printDebug('');
$this->printDebug(sprintf(
' <comment>%s)</comment> %s',
$i,
trim($header)
));
$i++;
}
} else {
$this->printDebug($body);
}
}
}
protected function printDebug($string)
{
if ($this->output === null) {
$this->output = new ConsoleOutput();
}
echo $string;
}
protected function assertResponsePropertiesExist(ResponseInterface $response, array $expectedProperties)
{
foreach ($expectedProperties as $propertyPath) {
// this will blow up if the property doesn't exist
$this->readResponseProperty($response, $propertyPath);
}
}
protected function assertResponsePropertyExists(ResponseInterface $response, $propertyPath)
{
// this will blow up if the property doesn't exist
$this->readResponseProperty($response, $propertyPath);
}
protected function assertResponsePropertyDoesNotExist(ResponseInterface $response, $propertyPath)
{
try {
// this will blow up if the property doesn't exist
$this->readResponseProperty($response, $propertyPath);
$this->fail(sprintf('Property "%s" exists, but it should not', $propertyPath));
} catch (RuntimeException $e) {
// cool, it blew up
// this catches all errors (but only errors) fro, the PropertyAccess component
}
}
protected function assertResponsePropertyEquals(ResponseInterface $response, $propertyPath, $expectedValue)
{
$actual = $this->readResponseProperty($response, $propertyPath);
$this->assertEquals(
$expectedValue,
$actual,
sprintf(
'Property "%s": Expected "%s" but response was "%s"',
$propertyPath,
$expectedValue,
var_export($actual, true)
)
);
}
protected function assertResponsePropertyContains(ResponseInterface $response, $propertyPath, $expectedValue)
{
$actualPropertValue = $this->readResponseProperty($response, $propertyPath);
$this->assertContains(
$expectedValue,
$actualPropertValue,
sprintf(
'Property "%s": Expected to contain "%s" but response was "%s"',
$propertyPath,
$expectedValue,
var_export($actualPropertValue, true)
)
);
}
protected function assertResponsePropertyIsArray(ResponseInterface $response, $propertyPath)
{
$this->assertInternalType('array', $this->readResponseProperty($response, $propertyPath));
}
protected function assertResponsePropertyCount(ResponseInterface $response, $propertyPath, $expectedCount)
{
$this->assertCount((int) $expectedCount, $this->readResponseProperty($response, $propertyPath));
}
/**
* @return EntityManager
*/
protected function getEntityManager()
{
return $this->getService('doctrine')
->getManager();
}
private function readResponseProperty(ResponseInterface $response, $propertyPath)
{
if ($this->accessor === null) {
$this->accessor = PropertyAccess::createPropertyAccessor();
}
$data = json_decode((string)$response->getBody());
try {
return $this->accessor->getValue($data, $propertyPath);
} catch (AccessException $e) {
// it could be a stdClass or an array of stdClass
$values = is_array($data) ? $data : get_object_vars($data);
throw new AccessException(sprintf(
'Error reading property "%s" from available keys (%s)',
$propertyPath,
implode(', ', array_keys($values))
), 0, $e);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment