diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
index 666831b041..e794b2b61d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php
@@ -23,7 +23,7 @@ use Symfony\Contracts\Service\ResetInterface;
*/
abstract class KernelTestCase extends TestCase
{
- use KernelShutdownOnTearDownTrait;
+ use TestCaseSetUpTearDownTrait;
protected static $class;
@@ -37,6 +37,11 @@ abstract class KernelTestCase extends TestCase
*/
protected static $container;
+ protected function doTearDown(): void
+ {
+ static::ensureKernelShutdown();
+ }
+
/**
* @return string The Kernel class name
*
diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelShutdownOnTearDownTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/TestCaseSetUpTearDownTrait.php
similarity index 53%
rename from src/Symfony/Bundle/FrameworkBundle/Test/KernelShutdownOnTearDownTrait.php
rename to src/Symfony/Bundle/FrameworkBundle/Test/TestCaseSetUpTearDownTrait.php
index bc23a39cfa..8fc0997913 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelShutdownOnTearDownTrait.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/TestCaseSetUpTearDownTrait.php
@@ -13,31 +13,60 @@ namespace Symfony\Bundle\FrameworkBundle\Test;
use PHPUnit\Framework\TestCase;
-// Auto-adapt to PHPUnit 8 that added a `void` return-type to the tearDown method
+// Auto-adapt to PHPUnit 8 that added a `void` return-type to the setUp/tearDown methods
if ((new \ReflectionMethod(TestCase::class, 'tearDown'))->hasReturnType()) {
/**
* @internal
*/
- trait KernelShutdownOnTearDownTrait
+ trait TestCaseSetUpTearDownTrait
{
+ private function doSetUp(): void
+ {
+ }
+
+ private function doTearDown(): void
+ {
+ }
+
+ protected function setUp(): void
+ {
+ $this->doSetUp();
+ }
+
protected function tearDown(): void
{
- static::ensureKernelShutdown();
+ $this->doTearDown();
}
}
} else {
/**
* @internal
*/
- trait KernelShutdownOnTearDownTrait
+ trait TestCaseSetUpTearDownTrait
{
+ private function doSetUp(): void
+ {
+ }
+
+ private function doTearDown(): void
+ {
+ }
+
+ /**
+ * @return void
+ */
+ protected function setUp()
+ {
+ $this->doSetUp();
+ }
+
/**
* @return void
*/
protected function tearDown()
{
- static::ensureKernelShutdown();
+ $this->doTearDown();
}
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/WebTestAssertions.php b/src/Symfony/Bundle/FrameworkBundle/Test/WebTestAssertions.php
new file mode 100644
index 0000000000..dbc6ef65ff
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/WebTestAssertions.php
@@ -0,0 +1,383 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Test;
+
+use PHPUnit\Framework\Assert;
+use Symfony\Bundle\FrameworkBundle\Client;
+use Symfony\Component\DomCrawler\Crawler;
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Ideas borrowed from Laravel Dusk's assertions.
+ *
+ * @see https://laravel.com/docs/5.7/dusk#available-assertions
+ */
+trait WebTestAssertions
+{
+ /** @var Client|null */
+ protected static $client;
+
+ public static function assertResponseIsSuccessful(): void
+ {
+ $response = static::getResponse();
+
+ Assert::assertTrue(
+ $response->isSuccessful(),
+ sprintf('Response was expected to be successful, but actual HTTP code is %d.', $response->getStatusCode())
+ );
+ }
+
+ public static function assertHttpCodeEquals(int $expectedCode): void
+ {
+ Assert::assertSame(
+ $expectedCode,
+ $code = static::getResponse()->getStatusCode(),
+ sprintf('Response code "%s" does not match actual HTTP code "%s".', $expectedCode, $code)
+ );
+ }
+
+ public static function assertResponseHasHeader(string $headerName): void
+ {
+ Assert::assertTrue(
+ static::getResponse()->headers->has($headerName),
+ sprintf('Header "%s" was not found in the Response.', $headerName)
+ );
+ }
+
+ public static function assertResponseNotHasHeader(string $headerName): void
+ {
+ Assert::assertFalse(
+ static::getResponse()->headers->has($headerName),
+ sprintf('Header "%s" was not expected to be found in the Response.', $headerName)
+ );
+ }
+
+ public static function assertResponseHeaderEquals(string $headerName, $expectedValue): void
+ {
+ Assert::assertSame(
+ $expectedValue,
+ $value = static::getResponse()->headers->get($headerName, null, true),
+ sprintf('Header "%s" with value "%s" does not equal actual value "%s".', $headerName, $expectedValue, $value)
+ );
+ }
+
+ public static function assertResponseHeaderNotEquals(string $headerName, $expectedValue): void
+ {
+ Assert::assertNotSame(
+ $expectedValue,
+ $value = static::getResponse()->headers->get($headerName, null, true),
+ sprintf('Header "%s" with value "%s" was not expected to equal actual value "%s".', $headerName, $expectedValue, $value)
+ );
+ }
+
+ public static function assertResponseRedirects(string $expectedLocation = null, int $expectedCode = null): void
+ {
+ $response = static::getResponse();
+
+ Assert::assertTrue(
+ $response->isRedirect(),
+ sprintf('Response was expected to be a redirection, but actual HTTP code is %s.', $response->getStatusCode())
+ );
+
+ if ($expectedCode) {
+ static::assertHttpCodeEquals($expectedCode);
+ }
+
+ if (null !== $expectedLocation) {
+ Assert::assertSame(
+ $expectedLocation,
+ $location = $response->headers->get('Location'),
+ sprintf('Location "%s" does not match actual redirection URL "%s".', $expectedLocation, $location)
+ );
+ }
+ }
+
+ public static function assertPageTitleEquals(string $expectedTitle): void
+ {
+ $titleNode = static::getCrawler()->filter('title');
+
+ Assert::assertSame(1, $count = $titleNode->count(), sprintf('There must be one
tag in the current page but there is actually %s.', $count));
+
+ Assert::assertEquals(
+ $expectedTitle,
+ trim($title = $titleNode->text()),
+ sprintf('Expected title "%s" does not equal actual title "%s".', $expectedTitle, $title)
+ );
+ }
+
+ public static function assertPageTitleContains(string $expectedTitle): void
+ {
+ $titleNode = static::getCrawler()->filter('title');
+
+ Assert::assertSame(1, $count = $titleNode->count(), sprintf('There must be one tag in the current page but there is actually %s.', $count));
+
+ Assert::assertContains(
+ $expectedTitle,
+ trim($title = $titleNode->text()),
+ sprintf('Expected title "%s" does not contain "%s".', $expectedTitle, $title)
+ );
+ }
+
+ public static function assertClientHasCookie(string $name, string $path = '/', string $domain = null): void
+ {
+ static::getClientForAssertion();
+
+ Assert::assertNotNull(
+ static::$client->getCookieJar()->get($name, $path, $domain),
+ sprintf('Did not find expected cookie "%s".', $name)
+ );
+ }
+
+ public static function assertClientNotHasCookie(string $name): void
+ {
+ static::getClientForAssertion();
+
+ $cookie = static::$client->getCookieJar()->get($name);
+
+ Assert::assertNull(
+ $cookie,
+ sprintf('Cookie "%s" was not expected to be set.', $name)
+ );
+ }
+
+ public static function assertClientCookieValueEquals(string $name, $expectedValue, string $path = '/', string $domain = null): void
+ {
+ static::getClientForAssertion();
+
+ $cookie = static::$client->getCookieJar()->get($name, $path, $domain);
+
+ Assert::assertNotNull(
+ $cookie,
+ sprintf('Did not find expected cookie "%s".', $name)
+ );
+ Assert::assertSame(
+ $expectedValue,
+ $value = $cookie->getValue(),
+ sprintf('Cookie name "%s" with value "%s" does not match actual value "%s".', $name, $expectedValue, $value)
+ );
+ }
+
+ public static function assertClientRawCookieValueEquals(string $name, $expectedValue, string $path = '/', string $domain = null): void
+ {
+ static::getClientForAssertion();
+
+ $cookie = static::$client->getCookieJar()->get($name, $path, $domain);
+
+ Assert::assertNotNull(
+ $cookie,
+ sprintf('Did not find expected cookie "%s".', $name)
+ );
+ Assert::assertSame(
+ $expectedValue,
+ $value = $cookie->getRawValue(),
+ sprintf('Cookie name "%s" with raw value "%s" does not match actual value "%s".', $name, $expectedValue, $value)
+ );
+ }
+
+ public static function assertResponseHasCookie(string $name, string $path = '/', string $domain = null): void
+ {
+ $cookie = static::getResponseCookieFromClient($name, $path, $domain);
+
+ Assert::assertNotNull(
+ $cookie,
+ sprintf('Did not find expected cookie "%s".', $name)
+ );
+ }
+
+ public static function assertResponseNotHasCookie(string $name, string $path = '/', string $domain = null): void
+ {
+ $cookie = static::getResponseCookieFromClient($name, $path, $domain);
+
+ Assert::assertNull(
+ $cookie,
+ sprintf('Cookie "%s" was not expected to be set.', $name)
+ );
+ }
+
+ public static function assertResponseCookieValueEquals(string $name, $expectedValue, string $path = '/', string $domain = null): void
+ {
+ $cookie = static::getResponseCookieFromClient($name, $path, $domain);
+
+ Assert::assertNotNull(
+ $cookie,
+ sprintf('Did not find expected cookie "%s".', $name)
+ );
+ Assert::assertSame(
+ $expectedValue,
+ $value = $cookie->getValue(),
+ sprintf('Cookie name "%s" with value "%s" does not match actual value "%s".', $name, $expectedValue, $value)
+ );
+ }
+
+ public static function assertResponseCookieValueNotEquals(string $name, $expectedValue, string $path = '/', string $domain = null): void
+ {
+ $cookie = static::getResponseCookieFromClient($name, $path, $domain);
+
+ Assert::assertNotNull(
+ $cookie,
+ sprintf('Did not find expected cookie "%s".', $name)
+ );
+ Assert::assertNotSame(
+ $expectedValue,
+ $value = $cookie->getValue(),
+ sprintf('Cookie name "%s" with value "%s" was not expected to be equal to actual value "%s".', $name, $expectedValue, $value)
+ );
+ }
+
+ public static function assertSelectorExists(string $selector): void
+ {
+ $nodes = static::getCrawler()->filter($selector);
+
+ Assert::assertGreaterThan(0, $nodes->count(), sprintf('Selector "%s" does not resolve to any node.', $selector));
+ }
+
+ public static function assertSelectorNotExists(string $selector): void
+ {
+ $nodes = static::getCrawler()->filter($selector);
+
+ Assert::assertEquals(0, $count = $nodes->count(), sprintf('Selector "%s" resolves to "%s" nodes where it expected 0.', $selector, $count));
+ }
+
+ public static function assertSelectorContainsText(string $selector, string $text): void
+ {
+ $nodes = static::getCrawler()->filter($selector);
+
+ Assert::assertGreaterThan(0, $nodes->count(), sprintf('Selector "%s" does not resolve to any node.', $selector));
+
+ Assert::assertContains($text, $nodes->text(), sprintf('Selector "%s" does not contain text "%s".', $selector, $text));
+ }
+
+ public static function assertSelectorNotContainsText(string $selector, string $text): void
+ {
+ $nodes = static::getCrawler()->filter($selector);
+
+ Assert::assertGreaterThan(0, $nodes->count(), sprintf('Selector "%s" does not resolve to any node.', $selector));
+
+ Assert::assertNotContains($text, $nodes->text(), sprintf('Selector "%s" was expected to not contain text "%s".', $selector, $text));
+ }
+
+ public static function assertInputValueEquals(string $fieldName, string $expectedValue): void
+ {
+ $inputNode = static::getCrawler()->filter("input[name=\"$fieldName\"]");
+
+ Assert::assertGreaterThan(0, $inputNode->count(), sprintf('Input with name "%s" not found on the page.', $fieldName));
+
+ Assert::assertEquals(
+ $expectedValue,
+ $value = $inputNode->getNode(0)->getAttribute('value'),
+ sprintf('Expected value "%s" for the "%s" input does not equal the actual value "%s".', $value, $fieldName, $value)
+ );
+ }
+
+ public static function assertInputValueNotEquals(string $fieldName, string $expectedValue): void
+ {
+ $inputNode = static::getCrawler()->filter("input[name=\"$fieldName\"]");
+
+ Assert::assertGreaterThan(0, $inputNode->count(), sprintf('Input with name "%s" not found on the page.', $fieldName));
+
+ Assert::assertNotEquals(
+ $expectedValue,
+ $value = $inputNode->getNode(0)->getAttribute('value'),
+ sprintf('Expected value "%s" for the "%s" input was expected to not equal the actual value "%s".', $value, $fieldName, $value)
+ );
+ }
+
+ public static function assertRouteEquals($expectedRoute, array $parameters = []): void
+ {
+ $request = static::checkRequestAvailable();
+
+ Assert::assertSame(
+ $expectedRoute,
+ $route = $request->attributes->get('_route'),
+ sprintf('Expected route name "%s" does not match the actual value "%s".', $expectedRoute, $route)
+ );
+
+ if (\count($parameters)) {
+ foreach ($parameters as $key => $expectedValue) {
+ static::assertRequestAttributeValueEquals($key, $expectedValue);
+ }
+ }
+ }
+
+ public static function assertRequestAttributeValueEquals(string $key, $expectedValue): void
+ {
+ $request = static::checkRequestAvailable();
+
+ Assert::assertSame(
+ $expectedValue,
+ $value = $request->attributes->get($key),
+ sprintf('Expected request attribute "%s" value "%s" does not match actual value "%s".', $key, $expectedValue, $value)
+ );
+ }
+
+ protected static function getResponseCookieFromClient(string $name, string $path = '/', string $domain = null): ?Cookie
+ {
+ $cookies = static::getResponse()->headers->getCookies();
+
+ $filteredCookies = array_filter($cookies, function (Cookie $cookie) use ($name, $path, $domain) {
+ return
+ $cookie->getName() === $name
+ && $cookie->getPath() === $path
+ && $cookie->getDomain() === $domain
+ ;
+ });
+
+ return reset($filteredCookies) ?: null;
+ }
+
+ private static function getClientForAssertion(): Client
+ {
+ if (!static::$client instanceof Client) {
+ static::fail(\sprintf(
+ 'A client must be set to make assertions on it. Did you forget to call "%s::createClient"?',
+ static::class
+ ));
+ }
+
+ return static::$client;
+ }
+
+ private static function getCrawler(): Crawler
+ {
+ $client = static::getClientForAssertion();
+
+ if (!$client->getCrawler()) {
+ static::fail('A client must have a crawler to make assertions. Did you forget to make an HTTP request?');
+ }
+
+ return $client->getCrawler();
+ }
+
+ private static function getResponse(): Response
+ {
+ $client = static::getClientForAssertion();
+
+ if (!$client->getResponse()) {
+ static::fail('A client must have an HTTP Response to make assertions. Did you forget to make an HTTP request?');
+ }
+
+ return $client->getResponse();
+ }
+
+ private static function checkRequestAvailable(): Request
+ {
+ $client = static::getClientForAssertion();
+
+ if (!$client->getRequest()) {
+ static::fail('A client must have an HTTP Request to make assertions. Did you forget to make an HTTP request?');
+ }
+
+ return $client->getRequest();
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php
index 5334d58361..cf6ab72cc5 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php
@@ -21,6 +21,16 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
*/
abstract class WebTestCase extends KernelTestCase
{
+ use WebTestAssertions;
+
+ protected function doTearDown(): void
+ {
+ parent::doTearDown();
+ if (static::$client) {
+ static::$client = null;
+ }
+ }
+
/**
* Creates a KernelBrowser.
*
@@ -44,6 +54,6 @@ abstract class WebTestCase extends KernelTestCase
$client->setServerParameters($server);
- return $client;
+ return static::$client = $client;
}
}
diff --git a/src/Symfony/Component/Form/Test/TestCaseSetUpTearDownTrait.php b/src/Symfony/Component/Form/Test/TestCaseSetUpTearDownTrait.php
index 30d41059b2..c7d785e0a8 100644
--- a/src/Symfony/Component/Form/Test/TestCaseSetUpTearDownTrait.php
+++ b/src/Symfony/Component/Form/Test/TestCaseSetUpTearDownTrait.php
@@ -45,17 +45,11 @@ if ((new \ReflectionMethod(TestCase::class, 'tearDown'))->hasReturnType()) {
*/
trait TestCaseSetUpTearDownTrait
{
- /**
- * @return void
- */
- private function doSetUp()
+ private function doSetUp(): void
{
}
- /**
- * @return void
- */
- private function doTearDown()
+ private function doTearDown(): void
{
}
diff --git a/src/Symfony/Component/Validator/Test/TestCaseSetUpTearDownTrait.php b/src/Symfony/Component/Validator/Test/TestCaseSetUpTearDownTrait.php
index be05bbb33f..ad8d38c97f 100644
--- a/src/Symfony/Component/Validator/Test/TestCaseSetUpTearDownTrait.php
+++ b/src/Symfony/Component/Validator/Test/TestCaseSetUpTearDownTrait.php
@@ -45,17 +45,11 @@ if ((new \ReflectionMethod(TestCase::class, 'tearDown'))->hasReturnType()) {
*/
trait TestCaseSetUpTearDownTrait
{
- /**
- * @return void
- */
- private function doSetUp()
+ private function doSetUp(): void
{
}
- /**
- * @return void
- */
- private function doTearDown()
+ private function doTearDown(): void
{
}