diff --git a/composer.json b/composer.json
index b5c6ffe2c872cd64244ba9b0c2aba03ade38787f..56303337885be9274637915dd9e137662e9c84ad 100644
--- a/composer.json
+++ b/composer.json
@@ -37,7 +37,7 @@
         "symfony/monolog-bridge": "^4.0",
         "symfony/doctrine-bridge": "^4.0",
         "symfony/options-resolver": "^4.0",
-        "symfony/phpunit-bridge": "^3.2",
+        "symfony/phpunit-bridge": "^5.0",
         "symfony/process": "^4.0",
         "symfony/serializer": "^4.0",
         "symfony/translation": "^4.0",
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 614f9d52a1cec435f618c2eb0ae2ca6feea45a3c..574790a68dd53c8b6f373b24ff707edebe04529f 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -1,10 +1,11 @@
 Changelog
 =========
 
-2.3.4 (XXXX-XX-XX)
+2.3.4 (2020-01-30)
 ------------------
 
- * Replace deprecated use of GetResponseForExceptionEvent->getException
+ * Prepare for PHPUnit version 9, avoid all warnings of PHPUnit version 8
+ * Replace internal Symfony classes with classes copied from Symfony
 
 2.3.3 (2019-11-25)
 ------------------
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 799f16c90700c65521c44a2017bc4c42655d8566..706c16d27e6c80c7fc4cd4ea8b93ee133ff2d012 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -8,7 +8,6 @@
          convertWarningsToExceptions="true"
          processIsolation="false"
          stopOnFailure="false"
-         syntaxCheck="false"
          bootstrap="vendor/autoload.php"
 >
     <testsuites>
diff --git a/src/Silex/Application.php b/src/Silex/Application.php
index a485a8c1460f4d817164cb5494340df55aaf460f..be3e5c7ee879f15d4d9388abed2c8536c615ec3a 100644
--- a/src/Silex/Application.php
+++ b/src/Silex/Application.php
@@ -41,7 +41,7 @@ use Silex\Provider\HttpKernelServiceProvider;
  */
 class Application extends Container implements HttpKernelInterface, TerminableInterface
 {
-    const VERSION = '2.3.4-DEV';
+    const VERSION = '2.3.4';
 
     const EARLY_EVENT = 512;
     const LATE_EVENT = -512;
diff --git a/src/Silex/EventListener/LogListener.php b/src/Silex/EventListener/LogListener.php
index d7c07029421a0da4190bf250141684237e157578..39c1b11209c9e16751bc72f8973735b8387533d8 100644
--- a/src/Silex/EventListener/LogListener.php
+++ b/src/Silex/EventListener/LogListener.php
@@ -35,7 +35,7 @@ class LogListener implements EventSubscriberInterface
     {
         $this->logger = $logger;
         if (null === $exceptionLogFilter) {
-            $exceptionLogFilter = function (\Throwable $e) {
+            $exceptionLogFilter = function (\Exception $e) {
                 if ($e instanceof HttpExceptionInterface && $e->getStatusCode() < 500) {
                     return LogLevel::ERROR;
                 }
@@ -82,7 +82,7 @@ class LogListener implements EventSubscriberInterface
      */
     public function onKernelException(GetResponseForExceptionEvent $event)
     {
-        $this->logException($event->getThrowable());
+        $this->logException($event->getException());
     }
 
     /**
@@ -114,7 +114,7 @@ class LogListener implements EventSubscriberInterface
     /**
      * Logs an exception.
      */
-    protected function logException(\Throwable $e)
+    protected function logException(\Exception $e)
     {
         $this->logger->log(call_user_func($this->exceptionLogFilter, $e), sprintf('%s: %s (uncaught exception) at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), ['exception' => $e]);
     }
diff --git a/src/Silex/ExceptionHandler.php b/src/Silex/ExceptionHandler.php
index 454656832a7607528472599ae19a60182e7205bb..0e2abc28858f74bb9e56d784b1723e35a2ae9820 100644
--- a/src/Silex/ExceptionHandler.php
+++ b/src/Silex/ExceptionHandler.php
@@ -11,8 +11,8 @@
 
 namespace Silex;
 
-use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
-use Symfony\Component\ErrorHandler\Exception\FlattenException;
+use Symfony\Component\Debug\ExceptionHandler as DebugExceptionHandler;
+use Symfony\Component\Debug\Exception\FlattenException;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
@@ -34,13 +34,14 @@ class ExceptionHandler implements EventSubscriberInterface
 
     public function onSilexError(GetResponseForExceptionEvent $event)
     {
-        $handler = new HtmlErrorRenderer($this->debug);
+        $handler = new DebugExceptionHandler($this->debug);
 
-        $exception = $event->getThrowable();
-        
-        $renderedException = $handler->render($exception);
+        $exception = $event->getException();
+        if (!$exception instanceof FlattenException) {
+            $exception = FlattenException::create($exception);
+        }
 
-        $response = Response::create($renderedException->getAsString(), $renderedException->getStatusCode(), $renderedException->getHeaders())->setCharset(ini_get('default_charset'));
+        $response = Response::create($handler->getHtml($exception), $exception->getStatusCode(), $exception->getHeaders())->setCharset(ini_get('default_charset'));
 
         $event->setResponse($response);
     }
diff --git a/src/Silex/ExceptionListenerWrapper.php b/src/Silex/ExceptionListenerWrapper.php
index 468fe3ed9727c16d569aba21947f2b6d05b82098..edb20d6b82a6edf09fd385d39a3b49b89085eacd 100644
--- a/src/Silex/ExceptionListenerWrapper.php
+++ b/src/Silex/ExceptionListenerWrapper.php
@@ -41,7 +41,7 @@ class ExceptionListenerWrapper
 
     public function __invoke(GetResponseForExceptionEvent $event)
     {
-        $exception = $event->getThrowable();
+        $exception = $event->getException();
         $this->callback = $this->app['callback_resolver']->resolveCallback($this->callback);
 
         if (!$this->shouldRun($exception)) {
diff --git a/src/Silex/Provider/Session/AbstractSessionListener.php b/src/Silex/Provider/Session/AbstractSessionListener.php
new file mode 100644
index 0000000000000000000000000000000000000000..a967dee3ee7319b4f8c6de6927b3a29f34374fd3
--- /dev/null
+++ b/src/Silex/Provider/Session/AbstractSessionListener.php
@@ -0,0 +1,151 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Silex\Provider\Session;
+
+use Psr\Container\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\KernelEvents;
+
+/**
+ * Sets the session onto the request on the "kernel.request" event and saves
+ * it on the "kernel.response" event.
+ *
+ * In addition, if the session has been started it overrides the Cache-Control
+ * header in such a way that all caching is disabled in that case.
+ * If you have a scenario where caching responses with session information in
+ * them makes sense, you can disable this behaviour by setting the header
+ * AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER on the response.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ * @author Tobias Schultze <http://tobion.de>
+ *
+ * Copied from Symfony, but removed internal. From namespace Symfony\Component\HttpKernel\EventListener
+ */
+abstract class AbstractSessionListener implements EventSubscriberInterface
+{
+    const NO_AUTO_CACHE_CONTROL_HEADER = 'Symfony-Session-NoAutoCacheControl';
+
+    protected $container;
+    private $sessionUsageStack = [];
+
+    public function __construct(ContainerInterface $container = null)
+    {
+        $this->container = $container;
+    }
+
+    public function onKernelRequest(GetResponseEvent $event)
+    {
+        if (!$event->isMasterRequest()) {
+            return;
+        }
+
+        $session = null;
+        $request = $event->getRequest();
+        if ($request->hasSession()) {
+            // no-op
+        } elseif (method_exists($request, 'setSessionFactory')) {
+            $request->setSessionFactory(function () { return $this->getSession(); });
+        } elseif ($session = $this->getSession()) {
+            $request->setSession($session);
+        }
+
+        $session = $session ?? ($this->container && $this->container->has('initialized_session') ? $this->container->get('initialized_session') : null);
+        $this->sessionUsageStack[] = $session instanceof Session ? $session->getUsageIndex() : 0;
+    }
+
+    public function onKernelResponse(FilterResponseEvent $event)
+    {
+        if (!$event->isMasterRequest()) {
+            return;
+        }
+
+        $response = $event->getResponse();
+        $autoCacheControl = !$response->headers->has(self::NO_AUTO_CACHE_CONTROL_HEADER);
+        // Always remove the internal header if present
+        $response->headers->remove(self::NO_AUTO_CACHE_CONTROL_HEADER);
+
+        if (!$session = $this->container && $this->container->has('initialized_session') ? $this->container->get('initialized_session') : $event->getRequest()->getSession()) {
+            return;
+        }
+
+        if ($session instanceof Session ? $session->getUsageIndex() !== end($this->sessionUsageStack) : $session->isStarted()) {
+            if ($autoCacheControl) {
+                $response
+                    ->setExpires(new \DateTime())
+                    ->setPrivate()
+                    ->setMaxAge(0)
+                    ->headers->addCacheControlDirective('must-revalidate');
+            }
+        }
+
+        if ($session->isStarted()) {
+            /*
+             * Saves the session, in case it is still open, before sending the response/headers.
+             *
+             * This ensures several things in case the developer did not save the session explicitly:
+             *
+             *  * If a session save handler without locking is used, it ensures the data is available
+             *    on the next request, e.g. after a redirect. PHPs auto-save at script end via
+             *    session_register_shutdown is executed after fastcgi_finish_request. So in this case
+             *    the data could be missing the next request because it might not be saved the moment
+             *    the new request is processed.
+             *  * A locking save handler (e.g. the native 'files') circumvents concurrency problems like
+             *    the one above. But by saving the session before long-running things in the terminate event,
+             *    we ensure the session is not blocked longer than needed.
+             *  * When regenerating the session ID no locking is involved in PHPs session design. See
+             *    https://bugs.php.net/61470 for a discussion. So in this case, the session must
+             *    be saved anyway before sending the headers with the new session ID. Otherwise session
+             *    data could get lost again for concurrent requests with the new ID. One result could be
+             *    that you get logged out after just logging in.
+             *
+             * This listener should be executed as one of the last listeners, so that previous listeners
+             * can still operate on the open session. This prevents the overhead of restarting it.
+             * Listeners after closing the session can still work with the session as usual because
+             * Symfonys session implementation starts the session on demand. So writing to it after
+             * it is saved will just restart it.
+             */
+            $session->save();
+        }
+    }
+
+    /**
+     * @internal
+     */
+    public function onFinishRequest(FinishRequestEvent $event)
+    {
+        if ($event->isMasterRequest()) {
+            array_pop($this->sessionUsageStack);
+        }
+    }
+
+    public static function getSubscribedEvents()
+    {
+        return [
+            KernelEvents::REQUEST => ['onKernelRequest', 128],
+            // low priority to come after regular response listeners, but higher than StreamedResponseListener
+            KernelEvents::RESPONSE => ['onKernelResponse', -1000],
+            KernelEvents::FINISH_REQUEST => ['onFinishRequest'],
+        ];
+    }
+
+    /**
+     * Gets the session object.
+     *
+     * @return SessionInterface|null A SessionInterface instance or null if no session is available
+     */
+    abstract protected function getSession();
+}
diff --git a/src/Silex/Provider/Session/AbstractTestSessionListener.php b/src/Silex/Provider/Session/AbstractTestSessionListener.php
new file mode 100644
index 0000000000000000000000000000000000000000..e7c592ff0f2c9b851c072fb28febb2390bf210b9
--- /dev/null
+++ b/src/Silex/Provider/Session/AbstractTestSessionListener.php
@@ -0,0 +1,114 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Silex\Provider\Session;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\KernelEvents;
+
+/**
+ * TestSessionListener.
+ *
+ * Saves session in test environment.
+ *
+ * @author Bulat Shakirzyanov <mallluhuct@gmail.com>
+ * @author Fabien Potencier <fabien@symfony.com>
+ * 
+ * Copied from Symfony, but removed internal. From namespace Symfony\Component\HttpKernel\EventListener
+ */
+abstract class AbstractTestSessionListener implements EventSubscriberInterface
+{
+    private $sessionId;
+    private $sessionOptions;
+
+    public function __construct(array $sessionOptions = [])
+    {
+        $this->sessionOptions = $sessionOptions;
+    }
+
+    public function onKernelRequest(GetResponseEvent $event)
+    {
+        if (!$event->isMasterRequest()) {
+            return;
+        }
+
+        // bootstrap the session
+        if (!$session = $this->getSession()) {
+            return;
+        }
+
+        $cookies = $event->getRequest()->cookies;
+
+        if ($cookies->has($session->getName())) {
+            $this->sessionId = $cookies->get($session->getName());
+            $session->setId($this->sessionId);
+        }
+    }
+
+    /**
+     * Checks if session was initialized and saves if current request is master
+     * Runs on 'kernel.response' in test environment.
+     */
+    public function onKernelResponse(FilterResponseEvent $event)
+    {
+        if (!$event->isMasterRequest()) {
+            return;
+        }
+
+        $request = $event->getRequest();
+        if (!$request->hasSession()) {
+            return;
+        }
+
+        $session = $request->getSession();
+        if ($wasStarted = $session->isStarted()) {
+            $session->save();
+        }
+
+        if ($session instanceof Session ? !$session->isEmpty() || (null !== $this->sessionId && $session->getId() !== $this->sessionId) : $wasStarted) {
+            $params = session_get_cookie_params() + ['samesite' => null];
+            foreach ($this->sessionOptions as $k => $v) {
+                if (0 === strpos($k, 'cookie_')) {
+                    $params[substr($k, 7)] = $v;
+                }
+            }
+
+            foreach ($event->getResponse()->headers->getCookies() as $cookie) {
+                if ($session->getName() === $cookie->getName() && $params['path'] === $cookie->getPath() && $params['domain'] == $cookie->getDomain()) {
+                    return;
+                }
+            }
+
+            $event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly'], false, $params['samesite'] ?: null));
+            $this->sessionId = $session->getId();
+        }
+    }
+
+    public static function getSubscribedEvents()
+    {
+        return [
+            KernelEvents::REQUEST => ['onKernelRequest', 192],
+            KernelEvents::RESPONSE => ['onKernelResponse', -128],
+        ];
+    }
+
+    /**
+     * Gets the session object.
+     *
+     * @return SessionInterface|null A SessionInterface instance or null if no session is available
+     */
+    abstract protected function getSession();
+}
diff --git a/src/Silex/Provider/Session/SessionListener.php b/src/Silex/Provider/Session/SessionListener.php
index a19355897f955159c9252237f345e012a6568360..281f0c9e9636a9fb0334f1a1bc55b7185f4f2ce8 100644
--- a/src/Silex/Provider/Session/SessionListener.php
+++ b/src/Silex/Provider/Session/SessionListener.php
@@ -12,7 +12,7 @@
 namespace Silex\Provider\Session;
 
 use Pimple\Container;
-use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener as BaseSessionListener;
+use Silex\Provider\Session\AbstractSessionListener as BaseSessionListener;
 
 /**
  * Sets the session in the request.
diff --git a/src/Silex/Provider/Session/TestSessionListener.php b/src/Silex/Provider/Session/TestSessionListener.php
index 3b64247c1e6602de8da664dcb3948b1d8c511b4e..47a18c8cd7da73cd05152f8e194d69f818795eec 100644
--- a/src/Silex/Provider/Session/TestSessionListener.php
+++ b/src/Silex/Provider/Session/TestSessionListener.php
@@ -12,7 +12,7 @@
 namespace Silex\Provider\Session;
 
 use Pimple\Container;
-use Symfony\Component\HttpKernel\EventListener\AbstractTestSessionListener as BaseTestSessionListener;
+use Silex\Provider\Session\AbstractTestSessionListener as BaseTestSessionListener;
 
 /**
  * Simulates sessions for testing purpose.
diff --git a/src/Silex/WebTestCase.php b/src/Silex/WebTestCase.php
index 8580b79cfd0a422ffa6ce2201e5587350aa1ffc7..1fb84f33cbd1695bfa052ad6923ded4144329ec6 100644
--- a/src/Silex/WebTestCase.php
+++ b/src/Silex/WebTestCase.php
@@ -35,7 +35,7 @@ abstract class WebTestCase extends TestCase
      * Note: Child classes that define a setUp method must call
      * parent::setUp().
      */
-    protected function setUp()
+    protected function setUp(): void
     {
         $this->app = $this->createApplication();
     }
diff --git a/tests/Silex/Tests/Application/SecurityTraitTest.php b/tests/Silex/Tests/Application/SecurityTraitTest.php
index 8918a100cb9e5c8f8e840005455fbd094f06d3c3..5d1b6662503a256e779b6fc4bb836f84fc3c92af 100644
--- a/tests/Silex/Tests/Application/SecurityTraitTest.php
+++ b/tests/Silex/Tests/Application/SecurityTraitTest.php
@@ -37,11 +37,11 @@ class SecurityTraitTest extends TestCase
     }
 
     /**
-     * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException
      */
     public function testIsGrantedWithoutTokenThrowsException()
     {
-        $app = $this->createApplication();
+    	$this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException::class);
+    	$app = $this->createApplication();
         $app->get('/', function () { return 'foo'; });
         $app->handle(Request::create('/'));
         $app->isGranted('ROLE_ADMIN');
diff --git a/tests/Silex/Tests/ApplicationTest.php b/tests/Silex/Tests/ApplicationTest.php
index 7b5bf5d78b84cd5a394c4f7ce830167bfe984270..baaea243fd5a38bedf64101e8b140219252218a1 100644
--- a/tests/Silex/Tests/ApplicationTest.php
+++ b/tests/Silex/Tests/ApplicationTest.php
@@ -408,11 +408,11 @@ class ApplicationTest extends TestCase
     }
 
     /**
-     * @expectedException \RuntimeException
      */
     public function testNonResponseAndNonNullReturnFromRouteBeforeMiddlewareShouldThrowRuntimeException()
     {
-        $app = new Application();
+    	$this->expectException(\RuntimeException::class);
+    	$app = new Application();
 
         $middleware = function (Request $request) {
             return 'string return';
@@ -427,11 +427,11 @@ class ApplicationTest extends TestCase
     }
 
     /**
-     * @expectedException \RuntimeException
      */
     public function testNonResponseAndNonNullReturnFromRouteAfterMiddlewareShouldThrowRuntimeException()
     {
-        $app = new Application();
+    	$this->expectException(\RuntimeException::class);
+    	$app = new Application();
 
         $middleware = function (Request $request) {
             return 'string return';
@@ -490,22 +490,22 @@ class ApplicationTest extends TestCase
     }
 
     /**
-     * @expectedException        \LogicException
-     * @expectedExceptionMessage The "mount" method takes either a "ControllerCollection" instance, "ControllerProviderInterface" instance, or a callable.
      */
     public function testMountNullException()
     {
-        $app = new Application();
+    	$this->expectException(\LogicException::class);
+    	$this->expectExceptionMessage('The "mount" method takes either a "ControllerCollection" instance, "ControllerProviderInterface" instance, or a callable.');
+    	$app = new Application();
         $app->mount('/exception', null);
     }
 
     /**
-     * @expectedException        \LogicException
-     * @expectedExceptionMessage The method "Silex\Tests\IncorrectControllerCollection::connect" must return a "ControllerCollection" instance. Got: "NULL"
      */
     public function testMountWrongConnectReturnValueException()
     {
-        $app = new Application();
+    	$this->expectException(\LogicException::class);
+    	$this->expectExceptionMessage('The method "Silex\Tests\IncorrectControllerCollection::connect" must return a "ControllerCollection" instance. Got: "NULL"');
+    	$app = new Application();
         $app->mount('/exception', new IncorrectControllerCollection());
     }
 
@@ -531,12 +531,12 @@ class ApplicationTest extends TestCase
     }
 
     /**
-     * @expectedException        \LogicException
-     * @expectedExceptionMessage The "homepage" route must have code to run when it matches.
      */
     public function testGetRouteCollectionWithRouteWithoutController()
     {
-        $app = new Application();
+    	$this->expectException(\LogicException::class);
+    	$this->expectExceptionMessage('The "homepage" route must have code to run when it matches.');
+    	$app = new Application();
         unset($app['exception_handler']);
         $app->match('/')->bind('homepage');
         $app->handle(Request::create('/'));
diff --git a/tests/Silex/Tests/CallbackResolverTest.php b/tests/Silex/Tests/CallbackResolverTest.php
index fc664759e9a910503c3f97f580dd12133eb38b4a..8128ef2019d88e30f077f3f99f37ed84afa15633 100644
--- a/tests/Silex/Tests/CallbackResolverTest.php
+++ b/tests/Silex/Tests/CallbackResolverTest.php
@@ -20,7 +20,7 @@ class CallbackResolverTest extends Testcase
     private $app;
     private $resolver;
 
-    public function setup()
+    public function setup(): void
     {
         $this->app = new Container();
         $this->resolver = new CallbackResolver($this->app);
@@ -61,13 +61,13 @@ class CallbackResolverTest extends Testcase
     }
 
     /**
-     * @expectedException          \InvalidArgumentException
-     * @expectedExceptionMessageRegExp  /Service "[a-z_]+" is not callable./
      * @dataProvider shouldThrowAnExceptionIfServiceIsNotCallableProvider
      */
     public function testShouldThrowAnExceptionIfServiceIsNotCallable($name)
     {
-        $this->app['non_callable_obj'] = function () { return new \stdClass(); };
+    	$this->expectException(\InvalidArgumentException::class);
+    	$this->expectExceptionMessageRegExp('/Service "[a-z_]+" is not callable./');
+    	$this->app['non_callable_obj'] = function () { return new \stdClass(); };
         $this->app['non_callable'] = function () { return []; };
         $this->resolver->convertCallback($name);
     }
diff --git a/tests/Silex/Tests/ControllerCollectionTest.php b/tests/Silex/Tests/ControllerCollectionTest.php
index 319ed7a34b41e9fc16504d4537642e5ca6f5e963..fd5b6a2ae4356da134c2d0d38076599bfdcbb3b7 100644
--- a/tests/Silex/Tests/ControllerCollectionTest.php
+++ b/tests/Silex/Tests/ControllerCollectionTest.php
@@ -174,12 +174,12 @@ class ControllerCollectionTest extends TestCase
     }
 
     /**
-     * @expectedException \LogicException
-     * @expectedExceptionMessage The "mount" method takes either a "ControllerCollection" instance or callable.
      */
     public function testMountCallableException()
     {
-        $controllers = new ControllerCollection(new Route());
+    	$this->expectException(\LogicException::class);
+    	$this->expectExceptionMessage('The "mount" method takes either a "ControllerCollection" instance or callable.');
+    	$controllers = new ControllerCollection(new Route());
         $controllers->mount('/prefix', '');
     }
 
@@ -286,11 +286,11 @@ class ControllerCollectionTest extends TestCase
     }
 
     /**
-     * @expectedException \BadMethodCallException
      */
     public function testRouteMethodDoesNotExist()
     {
-        $route = new MyRoute1();
+    	$this->expectException(\BadMethodCallException::class);
+    	$route = new MyRoute1();
 
         $controller = new ControllerCollection($route);
         $controller->bar();
diff --git a/tests/Silex/Tests/ControllerTest.php b/tests/Silex/Tests/ControllerTest.php
index ebf10b1eeb4e62b43f92ba93bd2da733e1fd1f77..72881bb78b56d2c8e4791ad02405ecd468a9995a 100644
--- a/tests/Silex/Tests/ControllerTest.php
+++ b/tests/Silex/Tests/ControllerTest.php
@@ -32,11 +32,11 @@ class ControllerTest extends TestCase
     }
 
     /**
-     * @expectedException \Silex\Exception\ControllerFrozenException
      */
     public function testBindOnFrozenControllerShouldThrowException()
     {
-        $controller = new Controller(new Route('/foo'));
+    	$this->expectException(\Silex\Exception\ControllerFrozenException::class);
+    	$controller = new Controller(new Route('/foo'));
         $controller->bind('foo');
         $controller->freeze();
         $controller->bind('bar');
@@ -111,11 +111,11 @@ class ControllerTest extends TestCase
     }
 
     /**
-     * @expectedException \BadMethodCallException
      */
     public function testRouteMethodDoesNotExist()
     {
-        $route = new MyRoute();
+    	$this->expectException(\BadMethodCallException::class);
+    	$route = new MyRoute();
 
         $controller = new Controller($route);
         $controller->bar();
diff --git a/tests/Silex/Tests/ExceptionHandlerTest.php b/tests/Silex/Tests/ExceptionHandlerTest.php
index 503a7c32ab0fe058c388e42f0c81859d8510a8c4..5ba783cab25df1e647456d72043100638591a9f4 100644
--- a/tests/Silex/Tests/ExceptionHandlerTest.php
+++ b/tests/Silex/Tests/ExceptionHandlerTest.php
@@ -35,7 +35,7 @@ class ExceptionHandlerTest extends TestCase
 
         $request = Request::create('/foo');
         $response = $app->handle($request);
-        $this->assertContains('Oops! An Error Occurred', $response->getContent());
+        $this->assertStringContainsString('Whoops, looks like something went wrong.', $response->getContent());
         $this->assertEquals(500, $response->getStatusCode());
     }
 
@@ -51,7 +51,7 @@ class ExceptionHandlerTest extends TestCase
         $request = Request::create('/foo');
         $response = $app->handle($request);
 
-        $this->assertContains('foo exception', $response->getContent());
+        $this->assertStringContainsString('foo exception', $response->getContent());
         $this->assertEquals(500, $response->getStatusCode());
     }
 
@@ -62,7 +62,7 @@ class ExceptionHandlerTest extends TestCase
 
         $request = Request::create('/foo');
         $response = $app->handle($request);
-        $this->assertContains('Not Found', $response->getContent());
+        $this->assertStringContainsString('Sorry, the page you are looking for could not be found.', $response->getContent());
         $this->assertEquals(404, $response->getStatusCode());
     }
 
@@ -73,7 +73,7 @@ class ExceptionHandlerTest extends TestCase
 
         $request = Request::create('/foo');
         $response = $app->handle($request);
-        $this->assertContains('No route found for "GET /foo"', html_entity_decode($response->getContent()));
+        $this->assertStringContainsString('No route found for "GET /foo"', html_entity_decode($response->getContent()));
         $this->assertEquals(404, $response->getStatusCode());
     }
 
@@ -86,7 +86,7 @@ class ExceptionHandlerTest extends TestCase
 
         $request = Request::create('/foo', 'POST');
         $response = $app->handle($request);
-        $this->assertContains('Oops! An Error Occurred', $response->getContent());
+        $this->assertStringContainsString('Whoops, looks like something went wrong.', $response->getContent());
         $this->assertEquals(405, $response->getStatusCode());
         $this->assertEquals('GET', $response->headers->get('Allow'));
     }
@@ -100,7 +100,7 @@ class ExceptionHandlerTest extends TestCase
 
         $request = Request::create('/foo', 'POST');
         $response = $app->handle($request);
-        $this->assertContains('No route found for "POST /foo": Method Not Allowed (Allow: GET)', html_entity_decode($response->getContent()));
+        $this->assertStringContainsString('No route found for "POST /foo": Method Not Allowed (Allow: GET)', html_entity_decode($response->getContent()));
         $this->assertEquals(405, $response->getStatusCode());
         $this->assertEquals('GET', $response->headers->get('Allow'));
     }
@@ -290,7 +290,7 @@ class ExceptionHandlerTest extends TestCase
 
         $request = Request::create('/foo');
         $response = $app->handle($request);
-        $this->assertContains('Exception thrown', $response->getContent());
+        $this->assertStringContainsString('Exception thrown', $response->getContent());
         $this->assertEquals(500, $response->getStatusCode());
     }
 
@@ -316,7 +316,7 @@ class ExceptionHandlerTest extends TestCase
 
         $request = Request::create('/foo');
         $response = $app->handle($request);
-        $this->assertContains('Caught Exception', $response->getContent());
+        $this->assertStringContainsString('Caught Exception', $response->getContent());
     }
 
     public function testExceptionHandlerWithSpecifiedException()
@@ -341,7 +341,7 @@ class ExceptionHandlerTest extends TestCase
 
         $request = Request::create('/foo');
         $response = $app->handle($request);
-        $this->assertContains('Caught LogicException', $response->getContent());
+        $this->assertStringContainsString('Caught LogicException', $response->getContent());
     }
 
     public function testExceptionHandlerWithSpecifiedExceptionInReverseOrder()
@@ -368,7 +368,7 @@ class ExceptionHandlerTest extends TestCase
 
         $request = Request::create('/foo');
         $response = $app->handle($request);
-        $this->assertContains('Caught Exception', $response->getContent());
+        $this->assertStringContainsString('Caught Exception', $response->getContent());
     }
 
     public function testExceptionHandlerWithArrayStyleCallback()
@@ -385,14 +385,14 @@ class ExceptionHandlerTest extends TestCase
 
         $request = Request::create('/foo');
         $response = $app->handle($request);
-        $this->assertContains('Caught Exception', $response->getContent());
+        $this->assertStringContainsString('Caught Exception', $response->getContent());
     }
 
     protected function checkRouteResponse($app, $path, $expectedContent, $method = 'get', $message = null)
     {
         $request = Request::create($path, $method);
         $response = $app->handle($request);
-        $this->assertEquals($expectedContent, $response->getContent(), $message);
+        $this->assertEquals($expectedContent, $response->getContent(), $message ?? '');
 
         return $response;
     }
diff --git a/tests/Silex/Tests/LazyRequestMatcherTest.php b/tests/Silex/Tests/LazyRequestMatcherTest.php
index 879d46fac0f2f2354145eb7d34d822845808c172..3fd5740b7212ef3635faf6f945efee8fab70ab46 100644
--- a/tests/Silex/Tests/LazyRequestMatcherTest.php
+++ b/tests/Silex/Tests/LazyRequestMatcherTest.php
@@ -43,12 +43,12 @@ class LazyRequestMatcherTest extends TestCase
     }
 
     /**
-     * @expectedException \LogicException
-     * @expectedExceptionMessage Factory supplied to LazyRequestMatcher must return implementation of Symfony\Component\Routing\RequestMatcherInterface.
      */
     public function testThatCanInjectRequestMatcherOnly()
     {
-        $matcher = new LazyRequestMatcher(function () {
+    	$this->expectException(\LogicException::class);
+    	$this->expectExceptionMessage('Factory supplied to LazyRequestMatcher must return implementation of Symfony\Component\Routing\RequestMatcherInterface.');
+    	$matcher = new LazyRequestMatcher(function () {
             return 'someMatcher';
         });
 
diff --git a/tests/Silex/Tests/Provider/FormServiceProviderTest.php b/tests/Silex/Tests/Provider/FormServiceProviderTest.php
index fa5c42d3911df909d69285f9003bcdc57086f1a0..f576700e7cb61edb96b6b2b7488d2a29adfdd5a4 100644
--- a/tests/Silex/Tests/Provider/FormServiceProviderTest.php
+++ b/tests/Silex/Tests/Provider/FormServiceProviderTest.php
@@ -84,12 +84,12 @@ class FormServiceProviderTest extends TestCase
     }
 
     /**
-     * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException
-     * @expectedExceptionMessage Invalid form type. The silex service "dummy" does not exist.
      */
     public function testNonExistentTypeService()
     {
-        $app = new Application();
+    	$this->expectException(\Symfony\Component\Form\Exception\InvalidArgumentException::class);
+    	$this->expectExceptionMessage('Invalid form type. The silex service "dummy" does not exist.');
+    	$app = new Application();
 
         $app->register(new FormServiceProvider());
 
@@ -154,32 +154,28 @@ class FormServiceProviderTest extends TestCase
         $app->register(new FormServiceProvider());
         
         $app->extend('form.types', function ($types) {
-            $types[] = new \Symfony\Component\Form\Tests\Fixtures\FooType();
             
             return $types;
         });
 
         $app->extend('form.type.extensions', function ($extensions) {
-            $extensions[] = new \Symfony\Component\Form\Tests\Fixtures\FooTypeBarExtension();
-            $extensions[] = new \Symfony\Component\Form\Tests\Fixtures\FooTypeBazExtension();
 
             return $extensions;
         });
         
         $form = $app['form.factory']->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', [])
-            ->add('dummy', 'Symfony\Component\Form\Tests\Fixtures\FooType')
             ->getForm();
 
         $this->assertInstanceOf('Symfony\Component\Form\Form', $form);
     }
 
     /**
-     * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException
-     * @expectedExceptionMessage Invalid form type extension. The silex service "dummy.form.type.extension" does not exist.
      */
     public function testNonExistentTypeExtensionService()
     {
-        $app = new Application();
+    	$this->expectException(\Symfony\Component\Form\Exception\InvalidArgumentException::class);
+    	$this->expectExceptionMessage('Invalid form type extension. The silex service "dummy.form.type.extension" does not exist.');
+    	$app = new Application();
 
         $app->register(new FormServiceProvider());
 
@@ -229,12 +225,12 @@ class FormServiceProviderTest extends TestCase
     }
 
     /**
-     * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException
-     * @expectedExceptionMessage Invalid form type guesser. The silex service "dummy.form.type.guesser" does not exist.
      */
     public function testNonExistentTypeGuesserService()
     {
-        $app = new Application();
+    	$this->expectException(\Symfony\Component\Form\Exception\InvalidArgumentException::class);
+    	$this->expectExceptionMessage('Invalid form type guesser. The silex service "dummy.form.type.guesser" does not exist.');
+    	$app = new Application();
 
         $app->register(new FormServiceProvider());
 
@@ -279,7 +275,7 @@ class FormServiceProviderTest extends TestCase
             $this->assertContains('ERROR: German translation', $form->getErrorsAsString());
         } else {
             // as of 2.5
-            $this->assertContains('ERROR: German translation', (string) $form->getErrors(true, false));
+        	$this->assertStringContainsString('ERROR: German translation', (string) $form->getErrors(true, false));
         }
     }
 
diff --git a/tests/Silex/Tests/Provider/MonologServiceProviderTest.php b/tests/Silex/Tests/Provider/MonologServiceProviderTest.php
index 408fb1c168453119f87fe8d8142ea5ffd83c2f21..3a7b9c1b070a7922df0cc69416537e7b535fbddb 100644
--- a/tests/Silex/Tests/Provider/MonologServiceProviderTest.php
+++ b/tests/Silex/Tests/Provider/MonologServiceProviderTest.php
@@ -30,13 +30,13 @@ class MonologServiceProviderTest extends TestCase
 {
     private $currErrorHandler;
 
-    protected function setUp()
+    protected function setUp(): void
     {
         $this->currErrorHandler = set_error_handler('var_dump');
         restore_error_handler();
     }
 
-    protected function tearDown()
+    protected function tearDown(): void
     {
         set_error_handler($this->currErrorHandler);
     }
@@ -58,7 +58,7 @@ class MonologServiceProviderTest extends TestCase
         $this->assertTrue($app['monolog.handler']->hasDebug('< 200'));
 
         $records = $app['monolog.handler']->getRecords();
-        $this->assertContains('Matched route "{route}".', $records[0]['message']);
+        $this->assertStringContainsString('Matched route "{route}".', $records[0]['message']);
         $this->assertSame('GET_foo', $records[0]['context']['route']);
     }
 
@@ -175,12 +175,12 @@ class MonologServiceProviderTest extends TestCase
     }
 
     /**
-     * @expectedException \InvalidArgumentException
-     * @expectedExceptionMessage Provided logging level 'foo' does not exist. Must be a valid monolog logging level.
      */
     public function testNonExistentStringErrorLevel()
     {
-        $app = $this->getApplication();
+    	$this->expectException(\InvalidArgumentException::class);
+    	$this->expectExceptionMessage("Provided logging level 'foo' does not exist. Must be a valid monolog logging level.");
+    	$app = $this->getApplication();
         $app['monolog.level'] = 'foo';
 
         $app['monolog.handler']->getLevel();
diff --git a/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php b/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php
index ccaa9aafd52675d1f2562fa54d5c1d6a56d1712e..26161aea4af383d2d91f985467ad2a908b1585b1 100644
--- a/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php
+++ b/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php
@@ -29,11 +29,11 @@ use Symfony\Component\HttpFoundation\Request;
 class SecurityServiceProviderTest extends WebTestCase
 {
     /**
-     * @expectedException \LogicException
      */
     public function testWrongAuthenticationType()
     {
-        $app = new Application();
+    	$this->expectException(\LogicException::class);
+    	$app = new Application();
         $app->register(new SecurityServiceProvider(), [
             'security.firewalls' => [
                 'wrong' => [
@@ -56,7 +56,7 @@ class SecurityServiceProviderTest extends WebTestCase
         $this->assertEquals('ANONYMOUS', $client->getResponse()->getContent());
 
         $client->request('post', '/login_check', ['_username' => 'fabien', '_password' => 'bar']);
-        $this->assertContains('Bad credentials', $app['security.last_error']($client->getRequest()));
+        $this->assertStringContainsString('Bad credentials', $app['security.last_error']($client->getRequest()));
         // hack to re-close the session as the previous assertions re-opens it
         $client->getRequest()->getSession()->save();
 
diff --git a/tests/Silex/Tests/Provider/ValidatorServiceProviderTest.php b/tests/Silex/Tests/Provider/ValidatorServiceProviderTest.php
index 02ff7f53b9a7658cf30e986ee9b9b4616bed82d2..a46e81c7d035c09b06722dcd2d6b05b4e232a9bf 100644
--- a/tests/Silex/Tests/Provider/ValidatorServiceProviderTest.php
+++ b/tests/Silex/Tests/Provider/ValidatorServiceProviderTest.php
@@ -207,6 +207,6 @@ class ValidatorServiceProviderTest extends TestCase
         $app->register(new ValidatorServiceProvider());
         $app->register(new TranslationServiceProvider());
 
-        $this->assertInternalType('array', $app['translator.resources']);
+        $this->assertIsArray($app['translator.resources']);
     }
 }
diff --git a/tests/Silex/Tests/RouterTest.php b/tests/Silex/Tests/RouterTest.php
index 5cf8cce32a8fdf4640e6186b8b369a290782e2d9..f930c6db60d3b1e5a390da5bbd301258cc52a5db 100644
--- a/tests/Silex/Tests/RouterTest.php
+++ b/tests/Silex/Tests/RouterTest.php
@@ -96,11 +96,11 @@ class RouterTest extends TestCase
     }
 
     /**
-     * @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
      */
     public function testMissingRoute()
     {
-        $app = new Application();
+    	$this->expectException(\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class);
+    	$app = new Application();
         unset($app['exception_handler']);
 
         $request = Request::create('/baz');
@@ -164,7 +164,7 @@ class RouterTest extends TestCase
         foreach (['/foo', '/bar'] as $path) {
             $request = Request::create($path);
             $response = $app->handle($request);
-            $this->assertContains($path, $response->getContent());
+            $this->assertStringContainsString($path, $response->getContent());
         }
     }
 
@@ -268,7 +268,7 @@ class RouterTest extends TestCase
     {
         $request = Request::create($path, $method);
         $response = $app->handle($request);
-        $this->assertEquals($expectedContent, $response->getContent(), $message);
+        $this->assertEquals($expectedContent, $response->getContent(), $message ?? '');
     }
 }
 
diff --git a/tests/Silex/Tests/ServiceControllerResolverRouterTest.php b/tests/Silex/Tests/ServiceControllerResolverRouterTest.php
index 4bc88a451a88ba4450d7eb00396bac29320b2094..7c93728c0d8952904f0c7c03d798a973a1e56801 100644
--- a/tests/Silex/Tests/ServiceControllerResolverRouterTest.php
+++ b/tests/Silex/Tests/ServiceControllerResolverRouterTest.php
@@ -38,6 +38,6 @@ class ServiceControllerResolverRouterTest extends RouterTest
     {
         $request = Request::create($path, $method);
         $response = $app->handle($request);
-        $this->assertEquals($expectedContent, $response->getContent(), $message);
+        $this->assertEquals($expectedContent, $response->getContent(), $message ?? '');
     }
 }
diff --git a/tests/Silex/Tests/ServiceControllerResolverTest.php b/tests/Silex/Tests/ServiceControllerResolverTest.php
index f5e446954c0ffe534e5c2d1401be9aa72bcdeeed..8337127d1929337ae06f125347cb55115c164fca 100644
--- a/tests/Silex/Tests/ServiceControllerResolverTest.php
+++ b/tests/Silex/Tests/ServiceControllerResolverTest.php
@@ -27,7 +27,7 @@ class ServiceControllerResolverTest extends Testcase
     private $mockResolver;
     private $resolver;
 
-    public function setup()
+    public function setup(): void
     {
         $this->mockResolver = $this->getMockBuilder('Symfony\Component\HttpKernel\Controller\ControllerResolverInterface')
             ->disableOriginalConstructor()