diff --git a/core/tests/Drupal/Tests/Listeners/AfterSymfonyListener.php b/core/tests/Drupal/Tests/Listeners/AfterSymfonyListener.php
new file mode 100644
index 0000000000000000000000000000000000000000..121d0d2db24677474ba0059defc56f6a5ed61382
--- /dev/null
+++ b/core/tests/Drupal/Tests/Listeners/AfterSymfonyListener.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Drupal\Tests\Listeners;
+
+use PHPUnit\Framework\Test;
+use PHPUnit\Framework\TestListener;
+use PHPUnit\Framework\TestListenerDefaultImplementation;
+
+if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) {
+  class_alias('Drupal\Tests\Listeners\Legacy\AfterSymfonyListener', 'Drupal\Tests\Listeners\AfterSymfonyListener');
+  // Using an early return instead of an else does not work when using the
+  // PHPUnit phar due to some weird PHP behavior (the class gets defined without
+  // executing the code before it and so the definition is not properly
+  // conditional).
+}
+else {
+  /**
+   * Listens to PHPUnit test runs.
+   *
+   * @internal
+   */
+  class AfterSymfonyListener implements TestListener {
+    use TestListenerDefaultImplementation;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function endTest(Test $test, $time) {
+      restore_error_handler();
+    }
+
+  }
+}
diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
index c67d7068d6b0c0670d81cc6b02db541d7dd98fe2..ef1d94d578ebb7fbdb280b6d1764a49c841441c0 100644
--- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
@@ -13,10 +13,21 @@
  *   fixed.
  */
 trait DeprecationListenerTrait {
+
   use ExpectDeprecationTrait;
 
+  /**
+   * The previous error handler.
+   *
+   * @var callable
+   */
+  private $previousHandler;
+
   protected function deprecationStartTest($test) {
     if ($test instanceof \PHPUnit_Framework_TestCase || $test instanceof TestCase) {
+      if ('disabled' !== getenv('SYMFONY_DEPRECATIONS_HELPER')) {
+        $this->registerErrorHandler($test);
+      }
       if ($this->willBeIsolated($test)) {
         putenv('DRUPAL_EXPECTED_DEPRECATIONS_SERIALIZE=' . tempnam(sys_get_temp_dir(), 'exdep'));
       }
@@ -126,6 +137,8 @@ public static function getSkippedDeprecations() {
       // is a Windows only deprecation. Remove when core no longer uses
       // WinCacheClassLoader in \Drupal\Core\DrupalKernel::initializeSettings().
       'The Symfony\Component\ClassLoader\WinCacheClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.',
+      // The following deprecation message is skipped for testing purposes.
+      '\Drupal\Tests\SkippedDeprecationTest deprecation',
       // These deprecations are triggered by symfony/psr-http-message-factory
       // 1.2, which can be installed if you update dependencies on php 7 or
       // higher
@@ -134,4 +147,40 @@ public static function getSkippedDeprecations() {
     ];
   }
 
+  /**
+   * Registers an error handler that wraps Symfony's DeprecationErrorHandler.
+   *
+   * @see \Symfony\Bridge\PhpUnit\DeprecationErrorHandler
+   * @see \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait
+   */
+  protected function registerErrorHandler($test) {
+    $deprecation_handler = function ($type, $msg, $file, $line, $context = []) {
+      // Skip listed deprecations.
+      if ($type === E_USER_DEPRECATED && in_array($msg, self::getSkippedDeprecations(), TRUE)) {
+        return;
+      }
+      return call_user_func($this->previousHandler, $type, $msg, $file, $line, $context);
+    };
+
+    if ($this->previousHandler) {
+      set_error_handler($deprecation_handler);
+      return;
+    }
+    $this->previousHandler = set_error_handler($deprecation_handler);
+
+    // Register another listener so that we can remove the error handler before
+    // Symfony's DeprecationErrorHandler checks that it is the currently
+    // registered handler. Note this is done like this to ensure the error
+    // handler is removed after SymfonyTestsListenerTrait::endTest() is called.
+    // SymfonyTestsListenerTrait has its own error handler that needs to be
+    // removed before this one.
+    $test_result_object = $test->getTestResultObject();
+    $reflection_class = new \ReflectionClass($test_result_object);
+    $reflection_property = $reflection_class->getProperty('listeners');
+    $reflection_property->setAccessible(TRUE);
+    $listeners = $reflection_property->getValue($test_result_object);
+    $listeners[] = new AfterSymfonyListener();
+    $reflection_property->setValue($test_result_object, $listeners);
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/Listeners/Legacy/AfterSymfonyListener.php b/core/tests/Drupal/Tests/Listeners/Legacy/AfterSymfonyListener.php
new file mode 100644
index 0000000000000000000000000000000000000000..fec2d78264d609993a3e7445bec623e0c9ce2660
--- /dev/null
+++ b/core/tests/Drupal/Tests/Listeners/Legacy/AfterSymfonyListener.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Drupal\Tests\Listeners\Legacy;
+
+/**
+ * Listens to PHPUnit test runs.
+ *
+ * @internal
+ */
+class AfterSymfonyListener extends \PHPUnit_Framework_BaseTestListener {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function endTest(\PHPUnit_Framework_Test $test, $time) {
+    restore_error_handler();
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/SkippedDeprecationTest.php b/core/tests/Drupal/Tests/SkippedDeprecationTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b65bdd22e3a19d69efe460bd027f8b1219b74bd8
--- /dev/null
+++ b/core/tests/Drupal/Tests/SkippedDeprecationTest.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\Tests;
+
+/**
+ * @group Test
+ */
+class SkippedDeprecationTest extends UnitTestCase {
+
+  /**
+   * Tests skipping deprecations in unit tests.
+   *
+   * @see \Drupal\Tests\Listeners\DeprecationListenerTrait::getSkippedDeprecations()
+   */
+  public function testSkippingDeprecations() {
+    @trigger_error('\Drupal\Tests\SkippedDeprecationTest deprecation', E_USER_DEPRECATED);
+    $this->addToAssertionCount(1);
+  }
+
+  /**
+   * Tests skipping deprecations in unit tests multiple times.
+   *
+   * @see \Drupal\Tests\Listeners\DeprecationListenerTrait::getSkippedDeprecations()
+   */
+  public function testSkippingDeprecationsAgain() {
+    @trigger_error('\Drupal\Tests\SkippedDeprecationTest deprecation', E_USER_DEPRECATED);
+    $this->addToAssertionCount(1);
+  }
+
+}