From bfabd9005621cb75e77cd297e7d9acb978d9d3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Thu, 27 Jun 2019 18:22:50 +0200 Subject: [PATCH 1/2] Add convenient testing assertions --- .gitignore | 1 + .php_cs.dist | 1 - composer.json | 7 +- phpstan.neon.dist | 3 + .../Bundle/Test/ApiTestAssertionsTrait.php | 109 +++++++++ .../Symfony/Bundle/Test/ApiTestCase.php | 15 ++ .../Bundle/Test/BrowserKitAssertionsTrait.php | 170 +++++++++++++ src/Bridge/Symfony/Bundle/Test/Client.php | 19 +- .../Bundle/Test/Constraint/ArraySubset.php | 129 ++++++++++ .../Test/Constraint/MatchesJsonSchema.php | 83 +++++++ src/Test/DoctrineMongoDbOdmFilterTestCase.php | 2 +- src/Test/DoctrineOrmFilterTestCase.php | 2 +- .../Extension/PaginationExtensionTest.php | 2 +- .../DataProvider/PaginatorTest.php | 2 +- .../Bundle/Command/SwaggerCommandTest.php | 12 +- .../RequestDataCollectorTest.php | 2 +- .../ApiPlatformExtensionTest.php | 4 +- .../DependencyInjection/ConfigurationTest.php | 2 +- .../Symfony/Bundle/Test/ApiTestCaseTest.php | 72 ++++++ .../Bridge/Symfony/Bundle/Test/ClientTest.php | 41 +++- .../Test/Constraint/ArraySubsetTest.php | 136 +++++++++++ .../Test/Constraint/MatchesJsonSchemaTest.php | 39 +++ .../Symfony/Bundle/Test/WebTestCaseTest.php | 227 ++++++++++++++++++ .../Twig/ApiPlatformProfilerPanelTest.php | 32 +-- .../ValidatorPropertyMetadataFactoryTest.php | 2 +- ...ansformFieldsetsParametersListenerTest.php | 2 +- ...ansformFilteringParametersListenerTest.php | 2 +- ...nsformPaginationParametersListenerTest.php | 2 +- ...TransformSortingParametersListenerTest.php | 2 +- .../ReservedAttributeNameConverterTest.php | 2 +- tests/JsonLd/ContextBuilderTest.php | 2 +- tests/Metadata/schema/XmlSchemaTest.php | 2 +- tests/Serializer/JsonEncoderTest.php | 2 +- tests/Serializer/ResourceListTest.php | 2 +- .../SerializerContextBuilderTest.php | 2 +- .../Serializer/ApiGatewayNormalizerTest.php | 2 +- .../AnnotationFilterExtractorTraitTest.php | 2 +- 37 files changed, 1090 insertions(+), 48 deletions(-) create mode 100644 src/Bridge/Symfony/Bundle/Test/ApiTestAssertionsTrait.php create mode 100644 src/Bridge/Symfony/Bundle/Test/BrowserKitAssertionsTrait.php create mode 100644 src/Bridge/Symfony/Bundle/Test/Constraint/ArraySubset.php create mode 100644 src/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchema.php create mode 100644 tests/Bridge/Symfony/Bundle/Test/ApiTestCaseTest.php create mode 100644 tests/Bridge/Symfony/Bundle/Test/Constraint/ArraySubsetTest.php create mode 100644 tests/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchemaTest.php create mode 100644 tests/Bridge/Symfony/Bundle/Test/WebTestCaseTest.php diff --git a/.gitignore b/.gitignore index 480cd68ad73..d187f4f7473 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /.php_cs /.php_cs.cache +/.phpunit.result.cache /build/ /composer.lock /composer.phar diff --git a/.php_cs.dist b/.php_cs.dist index dee1814e3a5..70aed101ac4 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -98,7 +98,6 @@ return PhpCsFixer\Config::create() 'phpdoc_trim_consecutive_blank_line_separation' => true, 'phpdoc_var_annotation_correct_order' => true, 'return_assignment' => true, - 'strict_comparison' => true, 'strict_param' => true, 'visibility_required' => [ 'elements' => [ diff --git a/composer.json b/composer.json index f9d313b33b4..0f3d94d7c1b 100644 --- a/composer.json +++ b/composer.json @@ -53,7 +53,7 @@ "phpstan/phpstan-doctrine": "^0.11", "phpstan/phpstan-phpunit": "^0.11", "phpstan/phpstan-symfony": "^0.11", - "phpunit/phpunit": "^7.5.2", + "phpunit/phpunit": "^8.0.0", "psr/log": "^1.0", "ramsey/uuid": "^3.7", "ramsey/uuid-doctrine": "^1.4", @@ -113,7 +113,10 @@ "autoload-dev": { "psr-4": { "ApiPlatform\\Core\\Tests\\": "tests/" - } + }, + "classmap": [ + "vendor/phpunit/phpunit/tests/" + ] }, "extra": { "branch-alias": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist index c71aa349b92..ee2b4c596a3 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -16,6 +16,9 @@ parameters: - %rootDir%/../../../tests/Fixtures/app/var/cache # The Symfony Configuration API isn't good enough to be analysed - %rootDir%/../../../src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php + # Imported code (temporary) + - %rootDir%/../../../src/Bridge/Symfony/Bundle/Test/BrowserKitAssertionsTrait.php + - %rootDir%/../../../tests/Bridge/Symfony/Bundle/Test/WebTestCaseTest.php ignoreErrors: # Real problems, hard to fix - '#Parameter \#2 \$dqlPart of method Doctrine\\ORM\\QueryBuilder::add\(\) expects array\|object, string given\.#' diff --git a/src/Bridge/Symfony/Bundle/Test/ApiTestAssertionsTrait.php b/src/Bridge/Symfony/Bundle/Test/ApiTestAssertionsTrait.php new file mode 100644 index 00000000000..4375dcbaf19 --- /dev/null +++ b/src/Bridge/Symfony/Bundle/Test/ApiTestAssertionsTrait.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Core\Bridge\Symfony\Bundle\Test; + +use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Constraint\ArraySubset; +use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Constraint\MatchesJsonSchema; +use PHPUnit\Framework\ExpectationFailedException; +use Symfony\Contracts\HttpClient\ResponseInterface; + +/** + * @see \Symfony\Bundle\FrameworkBundle\Test\WebTestAssertionsTrait + * + * @experimental + */ +trait ApiTestAssertionsTrait +{ + use BrowserKitAssertionsTrait; + + /** + * Asserts that the retrieved JSON contains has the specified subset. + * This method delegates to self::assertArraySubset(). + * + * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface + * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface + * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface + * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface + * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface + */ + public static function assertJsonContains(array $subset, bool $checkForObjectIdentity = true, string $message = ''): void + { + static::assertArraySubset($subset, self::getHttpResponse()->toArray(false), $checkForObjectIdentity, $message); + } + + /** + * Asserts that the retrieved JSON is equal to the following array. + * Both values are canonicalized before the comparision. + */ + public static function assertJsonEquals(array $json, string $message = ''): void + { + static::assertEqualsCanonicalizing($json, self::getHttpResponse()->toArray(false), $message); + } + + /** + * Asserts that an array has a specified subset. + * + * Imported from dms/phpunit-arraysubset, because the original constraint has been deprecated. + * + * @copyright Sebastian Bergmann + * @copyright Rafael Dohms + * + * @see https://github.com/sebastianbergmann/phpunit/issues/3494 + * + * @param iterable $subset + * @param iterable $array + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws \Exception + */ + public static function assertArraySubset($subset, $array, bool $checkForObjectIdentity = false, string $message = ''): void + { + $constraint = new ArraySubset($subset, $checkForObjectIdentity); + static::assertThat($array, $constraint, $message); + } + + /** + * @param array|string $jsonSchema + */ + public static function assertMatchesJsonSchema($jsonSchema, ?int $checkMode = null, string $message = ''): void + { + $constraint = new MatchesJsonSchema($jsonSchema, $checkMode); + static::assertThat(self::getHttpResponse()->toArray(false), $constraint, $message); + } + + private static function getHttpClient(Client $newClient = null): ?Client + { + static $client; + + if (0 < \func_num_args()) { + return $client = $newClient; + } + + if (!$client instanceof Client) { + static::fail(sprintf('A client must be set to make assertions on it. Did you forget to call "%s::createClient()"?', __CLASS__)); + } + + return $client; + } + + private static function getHttpResponse(): ResponseInterface + { + if (!$response = self::getHttpClient()->getResponse()) { + static::fail('A client must have an HTTP Response to make assertions. Did you forget to make an HTTP request?'); + } + + return $response; + } +} diff --git a/src/Bridge/Symfony/Bundle/Test/ApiTestCase.php b/src/Bridge/Symfony/Bundle/Test/ApiTestCase.php index 04f50c91623..9cb0c5daa8c 100644 --- a/src/Bridge/Symfony/Bundle/Test/ApiTestCase.php +++ b/src/Bridge/Symfony/Bundle/Test/ApiTestCase.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Core\Bridge\Symfony\Bundle\Test; +use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; @@ -25,6 +26,14 @@ */ abstract class ApiTestCase extends KernelTestCase { + use ApiTestAssertionsTrait; + + protected function doTearDown(): void + { + parent::doTearDown(); + self::getClient(null); + } + /** * Creates a Client. * @@ -40,9 +49,15 @@ protected static function createClient(array $options = []): Client */ $client = $kernel->getContainer()->get('test.api_platform.client'); } catch (ServiceNotFoundException $e) { + if (class_exists(KernelBrowser::class)) { + throw new \LogicException('You cannot create the client used in functional tests if the "framework.test" config is not set to true.'); + } throw new \LogicException('You cannot create the client used in functional tests if the BrowserKit component is not available. Try running "composer require symfony/browser-kit".'); } + self::getHttpClient($client); + self::getClient($client->getKernelBrowser()); + return $client; } } diff --git a/src/Bridge/Symfony/Bundle/Test/BrowserKitAssertionsTrait.php b/src/Bridge/Symfony/Bundle/Test/BrowserKitAssertionsTrait.php new file mode 100644 index 00000000000..dfb4d904971 --- /dev/null +++ b/src/Bridge/Symfony/Bundle/Test/BrowserKitAssertionsTrait.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ApiPlatform\Core\Bridge\Symfony\Bundle\Test; + +use PHPUnit\Framework\Constraint\LogicalAnd; +use PHPUnit\Framework\Constraint\LogicalNot; +use Symfony\Bundle\FrameworkBundle\KernelBrowser; +use Symfony\Component\BrowserKit\Test\Constraint as BrowserKitConstraint; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Test\Constraint as ResponseConstraint; + +/** + * Copied from Symfony, to remove when https://github.com/symfony/symfony/pull/32207 will be merged. + * + * @internal + */ +trait BrowserKitAssertionsTrait +{ + public static function assertResponseIsSuccessful(string $message = ''): void + { + self::assertThat(self::getResponse(), new ResponseConstraint\ResponseIsSuccessful(), $message); + } + + public static function assertResponseStatusCodeSame(int $expectedCode, string $message = ''): void + { + self::assertThat(self::getResponse(), new ResponseConstraint\ResponseStatusCodeSame($expectedCode), $message); + } + + public static function assertResponseRedirects(string $expectedLocation = null, int $expectedCode = null, string $message = ''): void + { + $constraint = new ResponseConstraint\ResponseIsRedirected(); + if ($expectedLocation) { + $constraint = LogicalAnd::fromConstraints($constraint, new ResponseConstraint\ResponseHeaderSame('Location', $expectedLocation)); + } + if ($expectedCode) { + $constraint = LogicalAnd::fromConstraints($constraint, new ResponseConstraint\ResponseStatusCodeSame($expectedCode)); + } + + self::assertThat(self::getResponse(), $constraint, $message); + } + + public static function assertResponseHasHeader(string $headerName, string $message = ''): void + { + self::assertThat(self::getResponse(), new ResponseConstraint\ResponseHasHeader($headerName), $message); + } + + public static function assertResponseNotHasHeader(string $headerName, string $message = ''): void + { + self::assertThat(self::getResponse(), new LogicalNot(new ResponseConstraint\ResponseHasHeader($headerName)), $message); + } + + public static function assertResponseHeaderSame(string $headerName, string $expectedValue, string $message = ''): void + { + self::assertThat(self::getResponse(), new ResponseConstraint\ResponseHeaderSame($headerName, $expectedValue), $message); + } + + public static function assertResponseHeaderNotSame(string $headerName, string $expectedValue, string $message = ''): void + { + self::assertThat(self::getResponse(), new LogicalNot(new ResponseConstraint\ResponseHeaderSame($headerName, $expectedValue)), $message); + } + + public static function assertResponseHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThat(self::getResponse(), new ResponseConstraint\ResponseHasCookie($name, $path, $domain), $message); + } + + public static function assertResponseNotHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThat(self::getResponse(), new LogicalNot(new ResponseConstraint\ResponseHasCookie($name, $path, $domain)), $message); + } + + public static function assertResponseCookieValueSame(string $name, string $expectedValue, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThat(self::getResponse(), LogicalAnd::fromConstraints( + new ResponseConstraint\ResponseHasCookie($name, $path, $domain), + new ResponseConstraint\ResponseCookieValueSame($name, $expectedValue, $path, $domain) + ), $message); + } + + public static function assertBrowserHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThat(self::getClient(), new BrowserKitConstraint\BrowserHasCookie($name, $path, $domain), $message); + } + + public static function assertBrowserNotHasCookie(string $name, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThat(self::getClient(), new LogicalNot(new BrowserKitConstraint\BrowserHasCookie($name, $path, $domain)), $message); + } + + public static function assertBrowserCookieValueSame(string $name, string $expectedValue, bool $raw = false, string $path = '/', string $domain = null, string $message = ''): void + { + self::assertThat(self::getClient(), LogicalAnd::fromConstraints( + new BrowserKitConstraint\BrowserHasCookie($name, $path, $domain), + new BrowserKitConstraint\BrowserCookieValueSame($name, $expectedValue, $raw, $path, $domain) + ), $message); + } + + public static function assertRequestAttributeValueSame(string $name, string $expectedValue, string $message = ''): void + { + self::assertThat(self::getRequest(), new ResponseConstraint\RequestAttributeValueSame($name, $expectedValue), $message); + } + + public static function assertRouteSame($expectedRoute, array $parameters = [], string $message = ''): void + { + $constraint = new ResponseConstraint\RequestAttributeValueSame('_route', $expectedRoute); + $constraints = []; + foreach ($parameters as $key => $value) { + $constraints[] = new ResponseConstraint\RequestAttributeValueSame($key, $value); + } + if ($constraints) { + $constraint = LogicalAnd::fromConstraints($constraint, ...$constraints); + } + + self::assertThat(self::getRequest(), $constraint, $message); + } + + private static function getClient(KernelBrowser $newClient = null): ?KernelBrowser + { + static $client; + + if (0 < \func_num_args()) { + return $client = $newClient; + } + + if (!$client instanceof KernelBrowser) { + static::fail(sprintf('A client must be set to make assertions on it. Did you forget to call "%s::createClient()"?', __CLASS__)); + } + + return $client; + } + + private static function getResponse(): Response + { + if (!$response = self::getClient()->getResponse()) { + static::fail('A client must have an HTTP Response to make assertions. Did you forget to make an HTTP request?'); + } + + return $response; + } + + private static function getRequest(): Request + { + if (!$request = self::getClient()->getRequest()) { + static::fail('A client must have an HTTP Request to make assertions. Did you forget to make an HTTP request?'); + } + + return $request; + } +} diff --git a/src/Bridge/Symfony/Bundle/Test/Client.php b/src/Bridge/Symfony/Bundle/Test/Client.php index a879f1f0f2e..5795eff695b 100644 --- a/src/Bridge/Symfony/Bundle/Test/Client.php +++ b/src/Bridge/Symfony/Bundle/Test/Client.php @@ -31,6 +31,11 @@ */ final class Client implements HttpClientInterface { + /** + * @var Response + */ + private $response; + /** * @see HttpClientInterface::OPTIONS_DEFAULTS */ @@ -100,7 +105,7 @@ public function request(string $method, string $url, array $options = []): Respo ]; $this->kernelBrowser->request($method, $resolvedUrl, [], [], $server, $options['body'] ?? null); - return new Response($this->kernelBrowser->getResponse(), $this->kernelBrowser->getInternalResponse(), $info); + return $this->response = new Response($this->kernelBrowser->getResponse(), $this->kernelBrowser->getInternalResponse(), $info); } /** @@ -111,8 +116,20 @@ public function stream($responses, float $timeout = null): ResponseStreamInterfa throw new \LogicException('Not implemented yet'); } + /** + * Gets the latest response. + * + * @internal + */ + public function getResponse(): ?Response + { + return $this->response; + } + /** * Gets the underlying test client. + * + * @internal */ public function getKernelBrowser(): KernelBrowser { diff --git a/src/Bridge/Symfony/Bundle/Test/Constraint/ArraySubset.php b/src/Bridge/Symfony/Bundle/Test/Constraint/ArraySubset.php new file mode 100644 index 00000000000..04d21d9d72a --- /dev/null +++ b/src/Bridge/Symfony/Bundle/Test/Constraint/ArraySubset.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Constraint; + +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\ExpectationFailedException; +use SebastianBergmann\Comparator\ComparisonFailure; +use SebastianBergmann\RecursionContext\InvalidArgumentException; + +/** + * Constraint that asserts that the array it is evaluated for has a specified subset. + * + * Uses array_replace_recursive() to check if a key value subset is part of the + * subject array. + * + * Imported from dms/phpunit-arraysubset-asserts, because the original constraint has been deprecated. + * + * @copyright Sebastian Bergmann + * @copyright Rafael Dohms + * + * @see https://github.com/sebastianbergmann/phpunit/issues/3494 + */ +final class ArraySubset extends Constraint +{ + private $subset; + private $strict; + + public function __construct(iterable $subset, bool $strict = false) + { + $this->strict = $strict; + $this->subset = $subset; + } + + /** + * Evaluates the constraint for parameter $other. + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param iterable $other + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * + * @return iterable|bool|null + */ + public function evaluate($other, string $description = '', bool $returnResult = false) + { + //type cast $other & $this->subset as an array to allow + //support in standard array functions. + $other = $this->toArray($other); + $this->subset = $this->toArray($this->subset); + $patched = array_replace_recursive($other, $this->subset); + if ($this->strict) { + $result = $other === $patched; + } else { + $result = $other == $patched; + } + if ($returnResult) { + return $result; + } + if ($result) { + return null; + } + + $f = new ComparisonFailure( + $patched, + $other, + var_export($patched, true), + var_export($other, true) + ); + $this->fail($other, $description, $f); + } + + /** + * Returns a string representation of the constraint. + * + * @throws InvalidArgumentException + */ + public function toString(): string + { + return 'has the subset '.$this->exporter()->export($this->subset); + } + + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws InvalidArgumentException + */ + protected function failureDescription($other): string + { + return 'an array '.$this->toString(); + } + + private function toArray(iterable $other): array + { + if (\is_array($other)) { + return $other; + } + if ($other instanceof \ArrayObject) { + return $other->getArrayCopy(); + } + if ($other instanceof \Traversable) { + return iterator_to_array($other); + } + // Keep BC even if we know that array would not be the expected one + return (array) $other; + } +} diff --git a/src/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchema.php b/src/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchema.php new file mode 100644 index 00000000000..eee5837667c --- /dev/null +++ b/src/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchema.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Constraint; + +use JsonSchema\Validator; +use PHPUnit\Framework\Constraint\Constraint; + +/** + * Asserts that a JSON document matches a given JSON Schema. + * + * @author Kévin Dunglas + * + * @experimental + */ +final class MatchesJsonSchema extends Constraint +{ + private $schema; + private $checkMode; + + /** + * @param array|string $schema + */ + public function __construct($schema, ?int $checkMode = null) + { + $this->checkMode = $checkMode; + $this->schema = \is_array($schema) ? (object) $schema : json_decode($schema); + } + + /** + * {@inheritdoc} + */ + public function toString(): string + { + return 'matches the provided JSON Schema'; + } + + /** + * @param array $other + */ + protected function matches($other): bool + { + if (!class_exists(Validator::class)) { + throw new \RuntimeException('The "justinrainbow/json-schema" library must be installed to use "assertMatchesJsonSchema()". Try running "composer require --dev justinrainbow/json-schema".'); + } + + $other = (object) $other; + + $validator = new Validator(); + $validator->validate($other, $this->schema, $this->checkMode); + + return $validator->isValid(); + } + + /** + * @param object $other + */ + protected function additionalFailureDescription($other): string + { + $other = (object) $other; + + $validator = new Validator(); + $validator->check($other, $this->schema); + + $errors = []; + foreach ($validator->getErrors() as $error) { + $property = $error['property'] ? $error['property'].': ' : ''; + $errors[] = $property.$error['message']; + } + + return implode("\n", $errors); + } +} diff --git a/src/Test/DoctrineMongoDbOdmFilterTestCase.php b/src/Test/DoctrineMongoDbOdmFilterTestCase.php index 31ac26dc9a1..7d2d8069df2 100644 --- a/src/Test/DoctrineMongoDbOdmFilterTestCase.php +++ b/src/Test/DoctrineMongoDbOdmFilterTestCase.php @@ -45,7 +45,7 @@ abstract class DoctrineMongoDbOdmFilterTestCase extends KernelTestCase */ protected $filterClass; - protected function setUp() + protected function setUp(): void { self::bootKernel(); diff --git a/src/Test/DoctrineOrmFilterTestCase.php b/src/Test/DoctrineOrmFilterTestCase.php index e0196f53dc0..4cd418c95f4 100644 --- a/src/Test/DoctrineOrmFilterTestCase.php +++ b/src/Test/DoctrineOrmFilterTestCase.php @@ -53,7 +53,7 @@ abstract class DoctrineOrmFilterTestCase extends KernelTestCase */ protected $filterClass; - protected function setUp() + protected function setUp(): void { self::bootKernel(); diff --git a/tests/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtensionTest.php b/tests/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtensionTest.php index 5eea4b3778f..1662d5a03b6 100644 --- a/tests/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtensionTest.php +++ b/tests/Bridge/Doctrine/MongoDbOdm/Extension/PaginationExtensionTest.php @@ -43,7 +43,7 @@ class PaginationExtensionTest extends TestCase { private $managerRegistryProphecy; - protected function setUp() + protected function setUp(): void { parent::setUp(); diff --git a/tests/Bridge/Elasticsearch/DataProvider/PaginatorTest.php b/tests/Bridge/Elasticsearch/DataProvider/PaginatorTest.php index 1c0a728aa45..684b2b7e3e5 100644 --- a/tests/Bridge/Elasticsearch/DataProvider/PaginatorTest.php +++ b/tests/Bridge/Elasticsearch/DataProvider/PaginatorTest.php @@ -147,7 +147,7 @@ function (array $document): Foo { ); } - protected function setUp() + protected function setUp(): void { $this->paginator = $this->getPaginator(4, 4); } diff --git a/tests/Bridge/Symfony/Bundle/Command/SwaggerCommandTest.php b/tests/Bridge/Symfony/Bundle/Command/SwaggerCommandTest.php index 69a7ce4ffe4..d4e230e327b 100644 --- a/tests/Bridge/Symfony/Bundle/Command/SwaggerCommandTest.php +++ b/tests/Bridge/Symfony/Bundle/Command/SwaggerCommandTest.php @@ -30,7 +30,7 @@ class SwaggerCommandTest extends KernelTestCase */ private $tester; - protected function setUp() + protected function setUp(): void { self::bootKernel(); @@ -70,7 +70,7 @@ public function testExecuteWithYamlVersion3() operationId: getDummyCarCollection YAML; - $this->assertContains($expected, $result, 'nested object should be present.'); + $this->assertStringContainsString($expected, $result, 'nested object should be present.'); $expected = <<assertContains($expected, $result, 'arrays should be correctly formatted.'); - $this->assertContains('openapi: 3.0.2', $result); + $this->assertStringContainsString($expected, $result, 'arrays should be correctly formatted.'); + $this->assertStringContainsString('openapi: 3.0.2', $result); $expected = <<assertContains($expected, $result, 'multiline formatting must be preserved (using literal style).'); + $this->assertStringContainsString($expected, $result, 'multiline formatting must be preserved (using literal style).'); } public function testExecuteOpenApiVersion2WithYaml() @@ -99,7 +99,7 @@ public function testExecuteOpenApiVersion2WithYaml() $result = $this->tester->getDisplay(); $this->assertYaml($result); - $this->assertContains("swagger: '2.0'", $result); + $this->assertStringContainsString("swagger: '2.0'", $result); } public function testExecuteWithBadArguments() diff --git a/tests/Bridge/Symfony/Bundle/DataCollector/RequestDataCollectorTest.php b/tests/Bridge/Symfony/Bundle/DataCollector/RequestDataCollectorTest.php index 3fbcfdac1cc..eed816dbbd6 100644 --- a/tests/Bridge/Symfony/Bundle/DataCollector/RequestDataCollectorTest.php +++ b/tests/Bridge/Symfony/Bundle/DataCollector/RequestDataCollectorTest.php @@ -47,7 +47,7 @@ class RequestDataCollectorTest extends TestCase private $metadataFactory; private $filterLocator; - protected function setUp() + protected function setUp(): void { $this->response = $this->createMock(Response::class); $this->attributes = $this->prophesize(ParameterBag::class); diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php index 4f45856f0cd..b896ce4da6d 100644 --- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php +++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php @@ -144,13 +144,13 @@ class ApiPlatformExtensionTest extends TestCase private $extension; private $childDefinitionProphecy; - protected function setUp() + protected function setUp(): void { $this->extension = new ApiPlatformExtension(); $this->childDefinitionProphecy = $this->prophesize(ChildDefinition::class); } - protected function tearDown() + protected function tearDown(): void { $this->extension = null; } diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php index 77c93265961..45523796686 100644 --- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php +++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php @@ -42,7 +42,7 @@ class ConfigurationTest extends TestCase */ private $processor; - protected function setUp() + protected function setUp(): void { $this->configuration = new Configuration(); $this->processor = new Processor(); diff --git a/tests/Bridge/Symfony/Bundle/Test/ApiTestCaseTest.php b/tests/Bridge/Symfony/Bundle/Test/ApiTestCaseTest.php new file mode 100644 index 00000000000..a8bf3fa2082 --- /dev/null +++ b/tests/Bridge/Symfony/Bundle/Test/ApiTestCaseTest.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Core\Tests\Bridge\Symfony\Bundle\Test; + +use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase; +use PHPUnit\Framework\ExpectationFailedException; + +class ApiTestCaseTest extends ApiTestCase +{ + public function testAssertJsonContains(): void + { + self::createClient()->request('GET', '/'); + $this->assertJsonContains(['@context' => '/contexts/Entrypoint']); + } + + public function testAssertJsonEquals(): void + { + self::createClient()->request('GET', '/contexts/Address'); + $this->assertJsonEquals([ + '@context' => [ + '@vocab' => 'http://example.com/docs.jsonld#', + 'hydra' => 'http://www.w3.org/ns/hydra/core#', + 'name' => 'Address/name', + ], + ]); + } + + public function testAssertMatchesJsonSchema(): void + { + $jsonSchema = <<request('GET', '/'); + $this->assertMatchesJsonSchema($jsonSchema); + $this->assertMatchesJsonSchema(json_decode($jsonSchema, true)); + } + + // Next tests have been imported from dms/phpunit-arraysubset-asserts, because the original constraint has been deprecated. + + public function testAssertArraySubsetPassesStrictConfig(): void + { + $this->expectException(ExpectationFailedException::class); + $this->assertArraySubset(['bar' => 0], ['bar' => '0'], true); + } + + public function testAssertArraySubsetDoesNothingForValidScenario(): void + { + $this->assertArraySubset([1, 2], [1, 2, 3]); + } +} diff --git a/tests/Bridge/Symfony/Bundle/Test/ClientTest.php b/tests/Bridge/Symfony/Bundle/Test/ClientTest.php index 1f1225d5837..2b5eba6acea 100644 --- a/tests/Bridge/Symfony/Bundle/Test/ClientTest.php +++ b/tests/Bridge/Symfony/Bundle/Test/ClientTest.php @@ -62,7 +62,8 @@ public function testCustomHeader(): void 'body' => '{"name": "Kevin"}', ]); $this->assertSame('application/xml; charset=utf-8', $response->getHeaders()['content-type'][0]); - $this->assertContains('Kevin', $response->getContent()); + $this->assertResponseHeaderSame('content-type', 'application/xml; charset=utf-8'); + $this->assertStringContainsString('Kevin', $response->getContent()); } /** @@ -74,6 +75,7 @@ public function testAuthBasic($basic): void $client->enableReboot(); $response = $client->request('GET', '/secured_dummies', ['auth_basic' => $basic]); $this->assertSame(200, $response->getStatusCode()); + $this->assertResponseIsSuccessful(); } public function authBasicProvider(): iterable @@ -82,6 +84,43 @@ public function authBasicProvider(): iterable yield [['dunglas', 'kevin']]; } + public function testComplexScenario(): void + { + self::createClient()->request('GET', '/secured_dummies', ['auth_basic' => ['dunglas', 'kevin']]); + $this->assertResponseIsSuccessful(); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + + $this->assertJsonEquals([ + '@context' => '/contexts/SecuredDummy', + '@id' => '/secured_dummies', + '@type' => 'hydra:Collection', + 'hydra:member' => [], + 'hydra:totalItems' => 0, + ]); + + $this->assertJsonContains( + [ + '@context' => '/contexts/SecuredDummy', + '@id' => '/secured_dummies', + ] + ); + + $this->assertMatchesJsonSchema(<<expectException(\LogicException::class); diff --git a/tests/Bridge/Symfony/Bundle/Test/Constraint/ArraySubsetTest.php b/tests/Bridge/Symfony/Bundle/Test/Constraint/ArraySubsetTest.php new file mode 100644 index 00000000000..087bd550b04 --- /dev/null +++ b/tests/Bridge/Symfony/Bundle/Test/Constraint/ArraySubsetTest.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Core\Tests\Bridge\Symfony\Bundle\Test\Constraint; + +use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Constraint\ArraySubset; +use ArrayAccessible; +use ArrayObject; +use Countable; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\SelfDescribing; +use PHPUnit\Framework\TestCase; +use ReflectionClass; +use SebastianBergmann\RecursionContext\InvalidArgumentException; +use Traversable; +use function sprintf; + +/** + * Imported from dms/phpunit-arraysubset-asserts, because the original constraint has been deprecated. + * + * @copyright Sebastian Bergmann + * @copyright Rafael Dohms + * + * @see https://github.com/sebastianbergmann/phpunit/issues/3494 + */ +final class ArraySubsetTest extends TestCase +{ + /** + * @return mixed[] + */ + public static function evaluateDataProvider(): array + { + return [ + 'loose array subset and array other' => [ + 'expected' => true, + 'subset' => ['bar' => 0], + 'other' => ['foo' => '', 'bar' => '0'], + 'strict' => false, + ], + 'strict array subset and array other' => [ + 'expected' => false, + 'subset' => ['bar' => 0], + 'other' => ['foo' => '', 'bar' => '0'], + 'strict' => true, + ], + 'loose array subset and ArrayObject other' => [ + 'expected' => true, + 'subset' => ['bar' => 0], + 'other' => new ArrayObject(['foo' => '', 'bar' => '0']), + 'strict' => false, + ], + 'strict ArrayObject subset and array other' => [ + 'expected' => true, + 'subset' => new ArrayObject(['bar' => 0]), + 'other' => ['foo' => '', 'bar' => 0], + 'strict' => true, + ], + ]; + } + + /** + * @param array|Traversable|mixed[] $subset + * @param array|Traversable|mixed[] $other + * @param bool $strict + * + * @throws ExpectationFailedException + * @throws InvalidArgumentException + * @dataProvider evaluateDataProvider + */ + public function testEvaluate(bool $expected, $subset, $other, $strict): void + { + $constraint = new ArraySubset($subset, $strict); + + $this->assertSame($expected, $constraint->evaluate($other, '', true)); + } + + public function testEvaluateWithArrayAccess(): void + { + $arrayAccess = new ArrayAccessible(['foo' => 'bar']); + + $constraint = new ArraySubset(['foo' => 'bar']); + + $this->assertTrue($constraint->evaluate($arrayAccess, '', true)); + } + + public function testEvaluateFailMessage(): void + { + $constraint = new ArraySubset(['foo' => 'bar']); + + try { + $constraint->evaluate(['baz' => 'bar'], '', false); + $this->fail(sprintf('Expected %s to be thrown.', ExpectationFailedException::class)); + } catch (ExpectationFailedException $expectedException) { + $comparisonFailure = $expectedException->getComparisonFailure(); + $this->assertNotNull($comparisonFailure); + $this->assertStringContainsString("'foo' => 'bar'", $comparisonFailure->getExpectedAsString()); + $this->assertStringContainsString("'baz' => 'bar'", $comparisonFailure->getActualAsString()); + } + } + + public function testIsCountable(): void + { + $reflection = new ReflectionClass(ArraySubset::class); + + $this->assertTrue( + $reflection->implementsInterface(Countable::class), + sprintf( + 'Failed to assert that ArraySubset implements "%s".', + Countable::class + ) + ); + } + + public function testIsSelfDescribing(): void + { + $reflection = new ReflectionClass(ArraySubset::class); + + $this->assertTrue( + $reflection->implementsInterface(SelfDescribing::class), + sprintf( + 'Failed to assert that Array implements "%s".', + SelfDescribing::class + ) + ); + } +} diff --git a/tests/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchemaTest.php b/tests/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchemaTest.php new file mode 100644 index 00000000000..4707390df3b --- /dev/null +++ b/tests/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchemaTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Core\Tests\Bridge\Symfony\Bundle\Test\Constraint; + +use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Constraint\MatchesJsonSchema; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\TestCase; + +class MatchesJsonSchemaTest extends TestCase +{ + public function testAdditionalFailureDescription(): void + { + $jsonSchema = <<evaluate(['foo' => 'bar']); + $this->fail(sprintf('Expected %s to be thrown.', ExpectationFailedException::class)); + } catch (ExpectationFailedException $expectedException) { + $this->assertStringContainsString('notexist: The property notexist is required', $expectedException->getMessage()); + } + } +} diff --git a/tests/Bridge/Symfony/Bundle/Test/WebTestCaseTest.php b/tests/Bridge/Symfony/Bundle/Test/WebTestCaseTest.php new file mode 100644 index 00000000000..a9603b76ddc --- /dev/null +++ b/tests/Bridge/Symfony/Bundle/Test/WebTestCaseTest.php @@ -0,0 +1,227 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Core\Tests\Bridge\Symfony\Bundle\Test; + +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\KernelBrowser; +use Symfony\Bundle\FrameworkBundle\Test\WebTestAssertionsTrait; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\BrowserKit\Cookie; +use Symfony\Component\BrowserKit\CookieJar; +use Symfony\Component\HttpFoundation\Cookie as HttpFoundationCookie; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Copied from Symfony, to remove when https://github.com/symfony/symfony/pull/32207 will be merged. + */ +class WebTestCaseTest extends TestCase +{ + public function testAssertResponseIsSuccessful() + { + $this->getResponseTester(new Response())->assertResponseIsSuccessful(); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage("Failed asserting that the Response is successful.\nHTTP/1.0 404 Not Found"); + $this->getResponseTester(new Response('', 404))->assertResponseIsSuccessful(); + } + + public function testAssertResponseStatusCodeSame() + { + $this->getResponseTester(new Response())->assertResponseStatusCodeSame(200); + $this->getResponseTester(new Response('', 404))->assertResponseStatusCodeSame(404); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage("Failed asserting that the Response status code is 200.\nHTTP/1.0 404 Not Found"); + $this->getResponseTester(new Response('', 404))->assertResponseStatusCodeSame(200); + } + + public function testAssertResponseRedirects() + { + $this->getResponseTester(new Response('', 301))->assertResponseRedirects(); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage("Failed asserting that the Response is redirected.\nHTTP/1.0 200 OK"); + $this->getResponseTester(new Response())->assertResponseRedirects(); + } + + public function testAssertResponseRedirectsWithLocation() + { + $this->getResponseTester(new Response('', 301, ['Location' => 'https://example.com/']))->assertResponseRedirects('https://example.com/'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('is redirected and has header "Location" with value "https://example.com/".'); + $this->getResponseTester(new Response('', 301))->assertResponseRedirects('https://example.com/'); + } + + public function testAssertResponseRedirectsWithStatusCode() + { + $this->getResponseTester(new Response('', 302))->assertResponseRedirects(null, 302); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('is redirected and status code is 301.'); + $this->getResponseTester(new Response('', 302))->assertResponseRedirects(null, 301); + } + + public function testAssertResponseRedirectsWithLocationAndStatusCode() + { + $this->getResponseTester(new Response('', 302, ['Location' => 'https://example.com/']))->assertResponseRedirects('https://example.com/', 302); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('is redirected and has header "Location" with value "https://example.com/" and status code is 301.'); + $this->getResponseTester(new Response('', 302))->assertResponseRedirects('https://example.com/', 301); + } + + public function testAssertResponseHasHeader() + { + $this->getResponseTester(new Response())->assertResponseHasHeader('Date'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('Failed asserting that the Response has header "X-Date".'); + $this->getResponseTester(new Response())->assertResponseHasHeader('X-Date'); + } + + public function testAssertResponseNotHasHeader() + { + $this->getResponseTester(new Response())->assertResponseNotHasHeader('X-Date'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('Failed asserting that the Response does not have header "Date".'); + $this->getResponseTester(new Response())->assertResponseNotHasHeader('Date'); + } + + public function testAssertResponseHeaderSame() + { + $this->getResponseTester(new Response())->assertResponseHeaderSame('Cache-Control', 'no-cache, private'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('Failed asserting that the Response has header "Cache-Control" with value "public".'); + $this->getResponseTester(new Response())->assertResponseHeaderSame('Cache-Control', 'public'); + } + + public function testAssertResponseHeaderNotSame() + { + $this->getResponseTester(new Response())->assertResponseHeaderNotSame('Cache-Control', 'public'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('Failed asserting that the Response does not have header "Cache-Control" with value "no-cache, private".'); + $this->getResponseTester(new Response())->assertResponseHeaderNotSame('Cache-Control', 'no-cache, private'); + } + + public function testAssertResponseHasCookie() + { + $response = new Response(); + $response->headers->setCookie(HttpFoundationCookie::create('foo', 'bar')); + + $this->getResponseTester($response)->assertResponseHasCookie('foo'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('Failed asserting that the Response has cookie "bar".'); + $this->getResponseTester($response)->assertResponseHasCookie('bar'); + } + + public function testAssertResponseNotHasCookie() + { + $response = new Response(); + $response->headers->setCookie(HttpFoundationCookie::create('foo', 'bar')); + + $this->getResponseTester($response)->assertResponseNotHasCookie('bar'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('Failed asserting that the Response does not have cookie "foo".'); + $this->getResponseTester($response)->assertResponseNotHasCookie('foo'); + } + + public function testAssertResponseCookieValueSame() + { + $response = new Response(); + $response->headers->setCookie(HttpFoundationCookie::create('foo', 'bar')); + + $this->getResponseTester($response)->assertResponseCookieValueSame('foo', 'bar'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('has cookie "bar" and has cookie "bar" with value "bar".'); + $this->getResponseTester($response)->assertResponseCookieValueSame('bar', 'bar'); + } + + public function testAssertBrowserHasCookie() + { + $this->getClientTester()->assertBrowserHasCookie('foo', '/path'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('Failed asserting that the Browser has cookie "bar".'); + $this->getClientTester()->assertBrowserHasCookie('bar'); + } + + public function testAssertBrowserNotHasCookie() + { + $this->getClientTester()->assertBrowserNotHasCookie('bar'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('Failed asserting that the Browser does not have cookie "foo" with path "/path".'); + $this->getClientTester()->assertBrowserNotHasCookie('foo', '/path'); + } + + public function testAssertBrowserCookieValueSame() + { + $this->getClientTester()->assertBrowserCookieValueSame('foo', 'bar', false, '/path'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('has cookie "foo" with path "/path" and has cookie "foo" with path "/path" with value "babar".'); + $this->getClientTester()->assertBrowserCookieValueSame('foo', 'babar', false, '/path'); + } + + public function testAssertRequestAttributeValueSame() + { + $this->getRequestTester()->assertRequestAttributeValueSame('foo', 'bar'); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('Failed asserting that the Request has attribute "foo" with value "baz".'); + $this->getRequestTester()->assertRequestAttributeValueSame('foo', 'baz'); + } + + public function testAssertRouteSame() + { + $this->getRequestTester()->assertRouteSame('homepage', ['foo' => 'bar']); + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('Failed asserting that the Request has attribute "_route" with value "articles".'); + $this->getRequestTester()->assertRouteSame('articles'); + } + + private function getResponseTester(Response $response): WebTestCase + { + $client = $this->createMock(KernelBrowser::class); + $client->expects($this->any())->method('getResponse')->willReturn($response); + + return $this->getTester($client); + } + + private function getClientTester(): WebTestCase + { + $client = $this->createMock(KernelBrowser::class); + $jar = new CookieJar(); + $jar->set(new Cookie('foo', 'bar', null, '/path', 'example.com')); + $client->expects($this->any())->method('getCookieJar')->willReturn($jar); + + return $this->getTester($client); + } + + private function getRequestTester(): WebTestCase + { + $client = $this->createMock(KernelBrowser::class); + $request = new Request(); + $request->attributes->set('foo', 'bar'); + $request->attributes->set('_route', 'homepage'); + $client->expects($this->any())->method('getRequest')->willReturn($request); + + return $this->getTester($client); + } + + private function getTester(KernelBrowser $client): WebTestCase + { + return new class($client) extends WebTestCase { + use WebTestAssertionsTrait; + + public function __construct(KernelBrowser $client) + { + parent::__construct(); + self::getClient($client); + } + }; + } +} diff --git a/tests/Bridge/Symfony/Bundle/Twig/ApiPlatformProfilerPanelTest.php b/tests/Bridge/Symfony/Bundle/Twig/ApiPlatformProfilerPanelTest.php index 0d1d1b301fb..bf6b7e199bc 100644 --- a/tests/Bridge/Symfony/Bundle/Twig/ApiPlatformProfilerPanelTest.php +++ b/tests/Bridge/Symfony/Bundle/Twig/ApiPlatformProfilerPanelTest.php @@ -38,7 +38,7 @@ class ApiPlatformProfilerPanelTest extends WebTestCase private $schemaTool; private $env; - protected function setUp() + protected function setUp(): void { parent::setUp(); $kernel = self::bootKernel(); @@ -56,7 +56,7 @@ protected function setUp() $this->schemaTool->createSchema($classes); } - protected function tearDown() + protected function tearDown(): void { $this->schemaTool->dropSchema($this->manager->getMetadataFactory()->getAllMetadata()); $this->manager->clear(); @@ -78,7 +78,7 @@ public function testDebugBarContentNotResourceClass() $block = $crawler->filter('div[class*=sf-toolbar-block-api_platform]'); // Check extra info content - $this->assertContains('sf-toolbar-status-default', $block->attr('class'), 'The toolbar block should have the default color.'); + $this->assertStringContainsString('sf-toolbar-status-default', $block->attr('class'), 'The toolbar block should have the default color.'); $this->assertSame('Not an API Platform resource', $block->filter('.sf-toolbar-info-piece span')->html()); } @@ -96,7 +96,7 @@ public function testDebugBarContent() $block = $crawler->filter('div[class*=sf-toolbar-block-api_platform]'); // Check extra info content - $this->assertContains('sf-toolbar-status-default', $block->attr('class'), 'The toolbar block should have the default color.'); + $this->assertStringContainsString('sf-toolbar-status-default', $block->attr('class'), 'The toolbar block should have the default color.'); $this->assertSame('mongodb' === $this->env ? DocumentDummy::class : Dummy::class, $block->filter('.sf-toolbar-info-piece span')->html()); } @@ -177,13 +177,13 @@ public function testGetCollectionProfiler() // Data provider tab $tabContent = $crawler->filter('.tab:nth-of-type(2) .tab-content'); $this->assertSame('TRUE', $tabContent->filter('table tbody .status-success')->html()); - $this->assertContains('mongodb' === $this->env ? OdmCollectionDataProvider::class : CollectionDataProvider::class, $tabContent->filter('table tbody')->html()); + $this->assertStringContainsString('mongodb' === $this->env ? OdmCollectionDataProvider::class : CollectionDataProvider::class, $tabContent->filter('table tbody')->html()); - $this->assertContains('No calls to item data provider have been recorded.', $tabContent->html()); - $this->assertContains('No calls to subresource data provider have been recorded.', $tabContent->html()); + $this->assertStringContainsString('No calls to item data provider have been recorded.', $tabContent->html()); + $this->assertStringContainsString('No calls to subresource data provider have been recorded.', $tabContent->html()); // Data persiters tab - $this->assertContains('No calls to data persister have been recorded.', $crawler->filter('.tab:nth-of-type(3) .tab-content .empty')->html()); + $this->assertStringContainsString('No calls to data persister have been recorded.', $crawler->filter('.tab:nth-of-type(3) .tab-content .empty')->html()); } public function testPostCollectionProfiler() @@ -202,14 +202,14 @@ public function testPostCollectionProfiler() // Data provider tab $tabContent = $crawler->filter('.tab:nth-of-type(2) .tab-content'); - $this->assertContains('No calls to collection data provider have been recorded.', $tabContent->html()); - $this->assertContains('No calls to item data provider have been recorded.', $tabContent->html()); - $this->assertContains('No calls to subresource data provider have been recorded.', $tabContent->html()); + $this->assertStringContainsString('No calls to collection data provider have been recorded.', $tabContent->html()); + $this->assertStringContainsString('No calls to item data provider have been recorded.', $tabContent->html()); + $this->assertStringContainsString('No calls to subresource data provider have been recorded.', $tabContent->html()); // Data persiters tab $tabContent = $crawler->filter('.tab:nth-of-type(3) .tab-content'); $this->assertSame('TRUE', $tabContent->filter('table tbody .status-success')->html()); - $this->assertContains(DataPersister::class, $tabContent->filter('table tbody')->html()); + $this->assertStringContainsString(DataPersister::class, $tabContent->filter('table tbody')->html()); } /** @@ -241,12 +241,12 @@ public function testGetItemProfiler() $this->assertSame(ContainNonResourceItemDataProvider::class, $tabContent->filter('table tbody tr:first-of-type td:nth-of-type(3)')->html()); $this->assertSame('TRUE', $tabContent->filter('table tbody .status-success')->html()); - $this->assertContains('mongodb' === $this->env ? OdmItemDataProvider::class : ItemDataProvider::class, $tabContent->filter('table tbody')->html()); + $this->assertStringContainsString('mongodb' === $this->env ? OdmItemDataProvider::class : ItemDataProvider::class, $tabContent->filter('table tbody')->html()); - $this->assertContains('No calls to collection data provider have been recorded.', $tabContent->html()); - $this->assertContains('No calls to subresource data provider have been recorded.', $tabContent->html()); + $this->assertStringContainsString('No calls to collection data provider have been recorded.', $tabContent->html()); + $this->assertStringContainsString('No calls to subresource data provider have been recorded.', $tabContent->html()); // Data persiters tab - $this->assertContains('No calls to data persister have been recorded.', $crawler->filter('.tab:nth-of-type(3) .tab-content .empty')->html()); + $this->assertStringContainsString('No calls to data persister have been recorded.', $crawler->filter('.tab:nth-of-type(3) .tab-content .empty')->html()); } } diff --git a/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php b/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php index b364b443b08..3ae3a29a27e 100644 --- a/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php +++ b/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php @@ -31,7 +31,7 @@ class ValidatorPropertyMetadataFactoryTest extends TestCase { private $validatorClassMetadata; - protected function setUp() + protected function setUp(): void { $this->validatorClassMetadata = new ClassMetadata(DummyValidatedEntity::class); (new AnnotationLoader(new AnnotationReader()))->loadClassMetadata($this->validatorClassMetadata); diff --git a/tests/JsonApi/EventListener/TransformFieldsetsParametersListenerTest.php b/tests/JsonApi/EventListener/TransformFieldsetsParametersListenerTest.php index 1da7b75ba3e..9e2d7133710 100644 --- a/tests/JsonApi/EventListener/TransformFieldsetsParametersListenerTest.php +++ b/tests/JsonApi/EventListener/TransformFieldsetsParametersListenerTest.php @@ -25,7 +25,7 @@ class TransformFieldsetsParametersListenerTest extends TestCase { private $listener; - protected function setUp() + protected function setUp(): void { $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); $resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata('dummy')); diff --git a/tests/JsonApi/EventListener/TransformFilteringParametersListenerTest.php b/tests/JsonApi/EventListener/TransformFilteringParametersListenerTest.php index 72a76c69a59..6420cf5e005 100644 --- a/tests/JsonApi/EventListener/TransformFilteringParametersListenerTest.php +++ b/tests/JsonApi/EventListener/TransformFilteringParametersListenerTest.php @@ -25,7 +25,7 @@ class TransformFilteringParametersListenerTest extends TestCase { private $listener; - protected function setUp() + protected function setUp(): void { $this->listener = new TransformFilteringParametersListener(); } diff --git a/tests/JsonApi/EventListener/TransformPaginationParametersListenerTest.php b/tests/JsonApi/EventListener/TransformPaginationParametersListenerTest.php index 54ae4b9fa35..5651a53454b 100644 --- a/tests/JsonApi/EventListener/TransformPaginationParametersListenerTest.php +++ b/tests/JsonApi/EventListener/TransformPaginationParametersListenerTest.php @@ -25,7 +25,7 @@ class TransformPaginationParametersListenerTest extends TestCase { private $listener; - protected function setUp() + protected function setUp(): void { $this->listener = new TransformPaginationParametersListener(); } diff --git a/tests/JsonApi/EventListener/TransformSortingParametersListenerTest.php b/tests/JsonApi/EventListener/TransformSortingParametersListenerTest.php index d1d9f51bf34..4f4e39613b3 100644 --- a/tests/JsonApi/EventListener/TransformSortingParametersListenerTest.php +++ b/tests/JsonApi/EventListener/TransformSortingParametersListenerTest.php @@ -25,7 +25,7 @@ class TransformSortingParametersListenerTest extends TestCase { private $listener; - protected function setUp() + protected function setUp(): void { $this->listener = new TransformSortingParametersListener(); } diff --git a/tests/JsonApi/Serializer/ReservedAttributeNameConverterTest.php b/tests/JsonApi/Serializer/ReservedAttributeNameConverterTest.php index 6a118cc048b..e2d6d5f269d 100644 --- a/tests/JsonApi/Serializer/ReservedAttributeNameConverterTest.php +++ b/tests/JsonApi/Serializer/ReservedAttributeNameConverterTest.php @@ -24,7 +24,7 @@ class ReservedAttributeNameConverterTest extends TestCase { private $reservedAttributeNameConverter; - protected function setUp() + protected function setUp(): void { $this->reservedAttributeNameConverter = new ReservedAttributeNameConverter(new CustomConverter()); } diff --git a/tests/JsonLd/ContextBuilderTest.php b/tests/JsonLd/ContextBuilderTest.php index 2a22e7d8860..03d790085e2 100644 --- a/tests/JsonLd/ContextBuilderTest.php +++ b/tests/JsonLd/ContextBuilderTest.php @@ -40,7 +40,7 @@ class ContextBuilderTest extends TestCase private $propertyMetadataFactoryProphecy; private $urlGeneratorProphecy; - protected function setUp() + protected function setUp(): void { $this->entityClass = '\Dummy\DummyEntity'; $this->resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class); diff --git a/tests/Metadata/schema/XmlSchemaTest.php b/tests/Metadata/schema/XmlSchemaTest.php index 43984b9f68c..6091e65ba3f 100644 --- a/tests/Metadata/schema/XmlSchemaTest.php +++ b/tests/Metadata/schema/XmlSchemaTest.php @@ -30,7 +30,7 @@ public function testSchema(): void XmlUtils::loadFile($fixtures.'invalid.xml', $schema); $this->fail(); } catch (\InvalidArgumentException $e) { - $this->assertContains('ERROR 1845', $e->getMessage()); + $this->assertStringContainsString('ERROR 1845', $e->getMessage()); } $this->assertInstanceOf(\DOMDocument::class, XmlUtils::loadFile($fixtures.'valid.xml', $schema)); diff --git a/tests/Serializer/JsonEncoderTest.php b/tests/Serializer/JsonEncoderTest.php index e52b28cfc0f..8ed79a789b6 100644 --- a/tests/Serializer/JsonEncoderTest.php +++ b/tests/Serializer/JsonEncoderTest.php @@ -26,7 +26,7 @@ class JsonEncoderTest extends TestCase */ private $encoder; - protected function setUp() + protected function setUp(): void { $this->encoder = new JsonEncoder('json'); } diff --git a/tests/Serializer/ResourceListTest.php b/tests/Serializer/ResourceListTest.php index 014c1c6a6fe..afa6ff2afbd 100644 --- a/tests/Serializer/ResourceListTest.php +++ b/tests/Serializer/ResourceListTest.php @@ -23,7 +23,7 @@ class ResourceListTest extends TestCase */ private $resourceList; - protected function setUp() + protected function setUp(): void { $this->resourceList = new ResourceList(); } diff --git a/tests/Serializer/SerializerContextBuilderTest.php b/tests/Serializer/SerializerContextBuilderTest.php index 72c68c8534e..dddb3a5d199 100644 --- a/tests/Serializer/SerializerContextBuilderTest.php +++ b/tests/Serializer/SerializerContextBuilderTest.php @@ -31,7 +31,7 @@ class SerializerContextBuilderTest extends TestCase */ private $builder; - protected function setUp() + protected function setUp(): void { $resourceMetadata = new ResourceMetadata( null, diff --git a/tests/Swagger/Serializer/ApiGatewayNormalizerTest.php b/tests/Swagger/Serializer/ApiGatewayNormalizerTest.php index 260f1f9ba49..f97c6f65078 100644 --- a/tests/Swagger/Serializer/ApiGatewayNormalizerTest.php +++ b/tests/Swagger/Serializer/ApiGatewayNormalizerTest.php @@ -39,7 +39,7 @@ final class ApiGatewayNormalizerTest extends TestCase */ private $normalizer; - protected function setUp() + protected function setUp(): void { $this->documentationNormalizerProphecy = $this->prophesize(NormalizerInterface::class); $this->documentationNormalizerProphecy->willImplement(CacheableSupportsMethodInterface::class); diff --git a/tests/Util/AnnotationFilterExtractorTraitTest.php b/tests/Util/AnnotationFilterExtractorTraitTest.php index 706566cead9..4a329ac0f42 100644 --- a/tests/Util/AnnotationFilterExtractorTraitTest.php +++ b/tests/Util/AnnotationFilterExtractorTraitTest.php @@ -28,7 +28,7 @@ class AnnotationFilterExtractorTraitTest extends KernelTestCase { private $extractor; - protected function setUp() + protected function setUp(): void { self::bootKernel(); $this->extractor = new AnnotationFilterExtractor(self::$kernel->getContainer()->get('test.annotation_reader')); From 12fe368dc6765a3135c0c537c98e88128a4fd705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Thu, 27 Jun 2019 23:28:08 +0200 Subject: [PATCH 2/2] Fix tests --- composer.json | 8 ++++---- .../Bridge/Symfony/Bundle/Test/ApiTestCaseTest.php | 13 +++++++++++++ tests/Bridge/Symfony/Bundle/Test/ClientTest.php | 5 +++++ .../Bundle/Test/Constraint/ArraySubsetTest.php | 10 +++++++++- .../Test/Constraint/MatchesJsonSchemaTest.php | 8 ++++++++ 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 0f3d94d7c1b..b0d5f2350a0 100644 --- a/composer.json +++ b/composer.json @@ -36,8 +36,8 @@ "doctrine/data-fixtures": "^1.2.2", "doctrine/doctrine-bundle": "^1.8", "doctrine/doctrine-cache-bundle": "^1.3.5", - "doctrine/mongodb-odm": "^2.0@beta", - "doctrine/mongodb-odm-bundle": "^4.0@beta", + "doctrine/mongodb-odm": "^2.0@rc", + "doctrine/mongodb-odm-bundle": "^4.0@rc", "doctrine/orm": "^2.6.3", "elasticsearch/elasticsearch": "^6.0", "friendsofsymfony/user-bundle": "^2.2@dev", @@ -53,7 +53,7 @@ "phpstan/phpstan-doctrine": "^0.11", "phpstan/phpstan-phpunit": "^0.11", "phpstan/phpstan-symfony": "^0.11", - "phpunit/phpunit": "^8.0.0", + "phpunit/phpunit": "^7.5.2 || ^8.0.0", "psr/log": "^1.0", "ramsey/uuid": "^3.7", "ramsey/uuid-doctrine": "^1.4", @@ -71,7 +71,7 @@ "symfony/expression-language": "^3.4 || ^4.0", "symfony/finder": "^3.4 || ^4.0", "symfony/form": "^3.4 || ^4.0", - "symfony/framework-bundle": "^4.3", + "symfony/framework-bundle": "^4.3.2", "symfony/http-client": "^4.3", "symfony/mercure-bundle": "*", "symfony/messenger": "^4.3", diff --git a/tests/Bridge/Symfony/Bundle/Test/ApiTestCaseTest.php b/tests/Bridge/Symfony/Bundle/Test/ApiTestCaseTest.php index a8bf3fa2082..24345839f11 100644 --- a/tests/Bridge/Symfony/Bundle/Test/ApiTestCaseTest.php +++ b/tests/Bridge/Symfony/Bundle/Test/ApiTestCaseTest.php @@ -15,11 +15,16 @@ use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase; use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Runner\Version; class ApiTestCaseTest extends ApiTestCase { public function testAssertJsonContains(): void { + if (version_compare(Version::id(), '8.0.0', '<')) { + $this->markTestSkipped('Requires PHPUnit 8'); + } + self::createClient()->request('GET', '/'); $this->assertJsonContains(['@context' => '/contexts/Entrypoint']); } @@ -61,12 +66,20 @@ public function testAssertMatchesJsonSchema(): void public function testAssertArraySubsetPassesStrictConfig(): void { + if (version_compare(Version::id(), '8.0.0', '<')) { + $this->markTestSkipped('Requires PHPUnit 8'); + } + $this->expectException(ExpectationFailedException::class); $this->assertArraySubset(['bar' => 0], ['bar' => '0'], true); } public function testAssertArraySubsetDoesNothingForValidScenario(): void { + if (version_compare(Version::id(), '8.0.0', '<')) { + $this->markTestSkipped('Requires PHPUnit 8'); + } + $this->assertArraySubset([1, 2], [1, 2, 3]); } } diff --git a/tests/Bridge/Symfony/Bundle/Test/ClientTest.php b/tests/Bridge/Symfony/Bundle/Test/ClientTest.php index 2b5eba6acea..5d742a1067d 100644 --- a/tests/Bridge/Symfony/Bundle/Test/ClientTest.php +++ b/tests/Bridge/Symfony/Bundle/Test/ClientTest.php @@ -17,6 +17,7 @@ use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Response; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Tools\SchemaTool; +use PHPUnit\Runner\Version; use Symfony\Component\HttpKernel\Profiler\Profile; class ClientTest extends ApiTestCase @@ -86,6 +87,10 @@ public function authBasicProvider(): iterable public function testComplexScenario(): void { + if (version_compare(Version::id(), '8.0.0', '<')) { + $this->markTestSkipped('Requires PHPUnit 8'); + } + self::createClient()->request('GET', '/secured_dummies', ['auth_basic' => ['dunglas', 'kevin']]); $this->assertResponseIsSuccessful(); $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); diff --git a/tests/Bridge/Symfony/Bundle/Test/Constraint/ArraySubsetTest.php b/tests/Bridge/Symfony/Bundle/Test/Constraint/ArraySubsetTest.php index 087bd550b04..a88914d74f7 100644 --- a/tests/Bridge/Symfony/Bundle/Test/Constraint/ArraySubsetTest.php +++ b/tests/Bridge/Symfony/Bundle/Test/Constraint/ArraySubsetTest.php @@ -20,6 +20,7 @@ use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\SelfDescribing; use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\Version; use ReflectionClass; use SebastianBergmann\RecursionContext\InvalidArgumentException; use Traversable; @@ -33,8 +34,15 @@ * * @see https://github.com/sebastianbergmann/phpunit/issues/3494 */ -final class ArraySubsetTest extends TestCase +class ArraySubsetTest extends TestCase { + protected function setUp(): void + { + if (version_compare(Version::id(), '8.0.0', '<')) { + $this->markTestSkipped('Requires PHPUnit 8'); + } + } + /** * @return mixed[] */ diff --git a/tests/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchemaTest.php b/tests/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchemaTest.php index 4707390df3b..13165f15d15 100644 --- a/tests/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchemaTest.php +++ b/tests/Bridge/Symfony/Bundle/Test/Constraint/MatchesJsonSchemaTest.php @@ -16,9 +16,17 @@ use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Constraint\MatchesJsonSchema; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\Version; class MatchesJsonSchemaTest extends TestCase { + protected function setUp(): void + { + if (version_compare(Version::id(), '8.0.0', '<')) { + $this->markTestSkipped('Requires PHPUnit 8'); + } + } + public function testAdditionalFailureDescription(): void { $jsonSchema = <<