Last active
May 12, 2023 20:24
-
-
Save devster/ecb6f4701f5d516300b385fe83618b16 to your computer and use it in GitHub Desktop.
OpenApi symfony testing trait
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Tests\Infrastructure\Action\Channel; | |
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; | |
use Tests\TestCase\OpenApiTrait; | |
class ExampleTest extends WebTestCase | |
{ | |
use OpenApiTrait; | |
public function testListing() | |
{ | |
static::useOpenApiSchema('channel'); | |
$client = static::createClient(); | |
$client->request('GET', '/channels'); | |
$this->assertOpenApi(); | |
$response = $client->getResponse(); | |
$this->assertTrue($response->isSuccessful()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace Tests\TestCase; | |
use League\OpenAPIValidation\PSR7\OperationAddress; | |
use League\OpenAPIValidation\PSR7\SchemaFactory\YamlFileFactory; | |
use League\OpenAPIValidation\PSR7\ValidatorBuilder; | |
use League\OpenAPIValidation\Schema\Exception\SchemaMismatch; | |
use Nyholm\Psr7\Factory\Psr17Factory; | |
use PHPUnit\Framework\AssertionFailedError; | |
use Psr\Http\Message\ResponseInterface; | |
use Psr\Http\Message\ServerRequestInterface; | |
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory; | |
use Symfony\Bundle\FrameworkBundle\KernelBrowser; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
trait OpenApiTrait | |
{ | |
public static array $openApiSpecFiles = []; | |
private static string $currentOpenApiFile; | |
private static array $validatorsBuilder = []; | |
private static PsrHttpFactory $psrHttpFactory; | |
private static KernelBrowser $client; | |
public static function useOpenApiSchema(string $name): void | |
{ | |
static::$currentOpenApiFile = $name; | |
} | |
public function assertOpenApiRequest(Request $request): OperationAddress | |
{ | |
try { | |
$psrRequest = static::createPsrRequest($request); | |
$op = static::getValidatorBuilder()->getRequestValidator()->validate($psrRequest); | |
$this->addToAssertionCount(1); | |
return $op; | |
} catch (\Throwable $e) { | |
throw static::createAssertionError('request', $e); | |
} | |
} | |
public function assertOpenApiResponse(OperationAddress $operationAddress, Response $response): void | |
{ | |
try { | |
$psrResponse = static::createPsrResponse($response); | |
static::getValidatorBuilder()->getResponseValidator()->validate($operationAddress, $psrResponse); | |
$this->addToAssertionCount(1); | |
} catch (\Throwable $e) { | |
throw static::createAssertionError('response', $e); | |
} | |
} | |
public function assertOpenApi(KernelBrowser $client = null): void | |
{ | |
$client = $client ?? static::$client; | |
$request = $client->getRequest(); | |
$response = $client->getResponse(); | |
$operationAddress = $this->assertOpenApiRequest($request); | |
$this->assertOpenApiResponse($operationAddress, $response); | |
} | |
/** | |
* Proxy to create a KernelBrowser and keep a reference of that client. | |
* | |
* @param array $options An array of options to pass to the createKernel method | |
* @param array $server An array of server parameters | |
* | |
* @return KernelBrowser A KernelBrowser instance | |
*/ | |
protected static function createClient(array $options = [], array $server = []) | |
{ | |
return static::$client = parent::createClient($options, $server); | |
} | |
private static function getValidatorBuilder(): ValidatorBuilder | |
{ | |
$name = static::$currentOpenApiFile; | |
if (!array_key_exists($name, static::$validatorsBuilder)) { | |
$schemaFactory = new YamlFileFactory(static::$openApiSpecFiles[$name]); | |
$validatorBuilder = new ValidatorBuilder(); | |
$validatorBuilder->fromSchema($schemaFactory->createSchema()); | |
static::$validatorsBuilder[$name] = $validatorBuilder; | |
} | |
return static::$validatorsBuilder[$name]; | |
} | |
private static function createPsrRequest(Request $request): ServerRequestInterface | |
{ | |
return static::getPsrHttpFactory()->createRequest($request); | |
} | |
private static function createPsrResponse(Response $response): ResponseInterface | |
{ | |
return static::getPsrHttpFactory()->createResponse($response); | |
} | |
private static function getPsrHttpFactory(): PsrHttpFactory | |
{ | |
if (!isset(static::$psrHttpFactory)) { | |
$psr17Factory = new Psr17Factory(); | |
static::$psrHttpFactory = new PsrHttpFactory($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory); | |
} | |
return static::$psrHttpFactory; | |
} | |
private static function createAssertionError(string $type, \Throwable $e): \Throwable | |
{ | |
$message = "OpenApi [$type] validation error:"; | |
$f = function (\Throwable $e) use (&$f) { | |
$message = "\n " . get_class($e) . ': '; | |
$message .= $e->getMessage(); | |
if ($e instanceof SchemaMismatch && $bc = $e->dataBreadCrumb()) { | |
$message .= "\n Breadcrumbs: " . implode(' -> ', $bc->buildChain()); | |
} | |
if ($p = $e->getPrevious()) { | |
$message .= $f($p); | |
} | |
return $message; | |
}; | |
$message .= $f($e); | |
return new AssertionFailedError($message); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
league/openapi-psr7-validator | |
nyholm/psr7 | |
symfony/psr-http-message-bridge |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once '../vendor/autoload.php'; | |
use OpenApiTrait; | |
OpenApiTrait::$openApiSpecFiles = [ | |
'channel' => __DIR__ . '/../documentation/channel/openapi.yaml', | |
]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment