diff --git a/core/modules/system/tests/modules/deprecation_test/deprecation_test.module b/core/modules/system/tests/modules/deprecation_test/deprecation_test.module
new file mode 100644
index 0000000000..fd1092a3c6
--- /dev/null
+++ b/core/modules/system/tests/modules/deprecation_test/deprecation_test.module
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * A deprecated function.
+ *
+ * @return string
+ *   A known return value of 'known_return_value'.
+ *
+ * @deprecated in Drupal 8.4.x. Might be removed before Drupal 9.0.0. This is
+ *   the deprecation message for deprecated_test_function().
+ */
+function deprecation_test_function() {
+  @trigger_error('This is the deprecation message for deprecation_test_function().', E_USER_DEPRECATED);
+  return 'known_return_value';
+}
diff --git a/core/modules/system/tests/modules/deprecation_test/deprecation_test.routing.yml b/core/modules/system/tests/modules/deprecation_test/deprecation_test.routing.yml
new file mode 100644
index 0000000000..3ef71b806f
--- /dev/null
+++ b/core/modules/system/tests/modules/deprecation_test/deprecation_test.routing.yml
@@ -0,0 +1,6 @@
+deprecation_test.route:
+  path: '/this-calls-a-deprecated-method'
+  defaults:
+    _controller: \Drupal\deprecation_test\DeprecatedController::deprecatedMethod
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/system/tests/modules/deprecation_test/src/DeprecatedController.php b/core/modules/system/tests/modules/deprecation_test/src/DeprecatedController.php
new file mode 100644
index 0000000000..44cf330cc8
--- /dev/null
+++ b/core/modules/system/tests/modules/deprecation_test/src/DeprecatedController.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\deprecation_test;
+
+/**
+ * Defines a controller that calls a deprecated method.
+ */
+class DeprecatedController {
+
+  /**
+   * Controller callback.
+   *
+   * @return array
+   *   Render array.
+   */
+  public function deprecatedMethod() {
+    return [
+      '#markup' => deprecation_test_function(),
+    ];
+  }
+
+}
diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist
index c85258d77f..3d004dbe6b 100644
--- a/core/phpunit.xml.dist
+++ b/core/phpunit.xml.dist
@@ -45,7 +45,7 @@
     </testsuite>
   </testsuites>
   <listeners>
-    <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener">
+    <listener class="\Drupal\Tests\Listeners\DrupalPhpUnitBridgeListener">
     </listener>
     <listener class="\Drupal\Tests\Listeners\DrupalStandardsListener">
     </listener>
diff --git a/core/tests/Drupal/FunctionalTests/Core/Test/PhpUnitBridgeTest.php b/core/tests/Drupal/FunctionalTests/Core/Test/PhpUnitBridgeTest.php
new file mode 100644
index 0000000000..0050f8ed1e
--- /dev/null
+++ b/core/tests/Drupal/FunctionalTests/Core/Test/PhpUnitBridgeTest.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\FunctionalTests\Core\Test;
+
+use Drupal\Core\Url;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests Drupal's integration with Symfony PHPUnit Bridge.
+ *
+ * @group Test
+ * @group legacy
+ */
+class PhpUnitBridgeTest extends BrowserTestBase {
+
+  protected static $modules = ['deprecation_test'];
+
+  /**
+   * @expectedDeprecation This is the deprecation message for deprecation_test_function().
+   */
+  public function testSilencedError() {
+    $this->assertEquals('known_return_value', deprecation_test_function());
+  }
+
+  /**
+   * @todo Add (at)expectedDeprecation This is the deprecation message for
+   *   deprecation_test_function().
+   */
+  public function testErrorOnSiteUnderTest() {
+    $this->markTestIncomplete('Restore this test to have an @expectedDeprecation annotation when page controllers do not swallow E_USER_DEPRECATED errors.');
+    $this->drupalGet(Url::fromRoute('deprecation_test.route'));
+  }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Test/PhpUnitBridgeTest.php b/core/tests/Drupal/KernelTests/Core/Test/PhpUnitBridgeTest.php
new file mode 100644
index 0000000000..d82a37b906
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Test/PhpUnitBridgeTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Drupal\KernelTests\Core\Test;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\deprecation_test\Deprecation\FixtureDeprecatedClass;
+
+/**
+ * Test how kernel tests interact with deprecation errors.
+ *
+ * @group Test
+ * @group legacy
+ */
+class PhpUnitBridgeTest extends KernelTestBase {
+
+  public static $modules = ['deprecation_test'];
+
+  /**
+   * @expectedDeprecation Drupal\deprecation_test\Deprecation\FixtureDeprecatedClass is deprecated.
+   */
+  public function testDeprecatedClass() {
+    $deprecated = new FixtureDeprecatedClass();
+    $this->assertEquals('test', $deprecated->testFunction());
+  }
+
+  /**
+   * @expectedDeprecation This is the deprecation message for deprecation_test_function().
+   */
+  public function testDeprecatedFunction() {
+    $this->assertEquals('known_return_value', \deprecation_test_function());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeIsolatedTest.php b/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeIsolatedTest.php
new file mode 100644
index 0000000000..72806b44c3
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeIsolatedTest.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Drupal\Tests\Core\Test;
+
+use Drupal\Tests\UnitTestCase;
+use Drupal\deprecation_test\Deprecation\FixtureDeprecatedClass;
+
+/**
+ * Test how unit tests interact with deprecation errors in process isolation.
+ *
+ * @runTestsInSeparateProcesses
+ *
+ * @group Test
+ * @group legacy
+ */
+class PhpUnitBridgeIsolatedTest extends UnitTestCase {
+
+  /**
+   * @expectedDeprecation Drupal\deprecation_test\Deprecation\FixtureDeprecatedClass is deprecated.
+   */
+  public function testDeprecatedClass() {
+    $deprecated = new FixtureDeprecatedClass();
+    $this->assertEquals('test', $deprecated->testFunction());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeTest.php b/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeTest.php
new file mode 100644
index 0000000000..48e900564f
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeTest.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\Tests\Core\Test;
+
+use Drupal\Tests\UnitTestCase;
+use Drupal\deprecation_test\Deprecation\FixtureDeprecatedClass;
+
+/**
+ * Test how unit tests interact with deprecation errors.
+ *
+ * @group Test
+ * @group legacy
+ */
+class PhpUnitBridgeTest extends UnitTestCase {
+
+  /**
+   * @expectedDeprecation Drupal\deprecation_test\Deprecation\FixtureDeprecatedClass is deprecated.
+   */
+  public function testDeprecatedClass() {
+    $deprecated = new FixtureDeprecatedClass();
+    $this->assertEquals('test', $deprecated->testFunction());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Listeners/DrupalPhpUnitBridgeListener.php b/core/tests/Drupal/Tests/Listeners/DrupalPhpUnitBridgeListener.php
new file mode 100644
index 0000000000..df2c2911dd
--- /dev/null
+++ b/core/tests/Drupal/Tests/Listeners/DrupalPhpUnitBridgeListener.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\Tests\Listeners;
+
+use Symfony\Bridge\PhpUnit\SymfonyTestsListener;
+
+/**
+ * Wrapper listener for SymfonyTestsListener.
+ *
+ * Because SymfonyTestsListener does not adequately handle tests which are run
+ * in isolation, we'll mark tests with @group legacy and @expectedDeprecation
+ * annotation as risky.
+ *
+ * @see https://www.drupal.org/node/2870194
+ * @see https://github.com/symfony/symfony/issues/23003
+ *
+ * @todo This wrapper class should be removed when Symfony can handle these
+ * use-cases upstream.
+ */
+class DrupalPhpUnitBridgeListener extends SymfonyTestsListener {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function endTest(\PHPUnit_Framework_Test $test, $time) {
+    // If the test ran in isolation, we'll check if there were any expected
+    // deprecations, and if there are, we'll mark this test as risky.
+    if ($this->wouldRunInIsolation($test)) {
+      if (!function_exists('__phpunit_run_isolated_test')) {
+        // Since SymfonyTestsListener's list of expected deprecations is
+        // private, we have to re-parse the annotations here.
+        $expectedDeprecations = [];
+        $annotations = \PHPUnit_Util_Test::parseTestMethodAnnotations(get_class($test), $test->getName(false));
+        if (isset($annotations['method']['expectedDeprecation'])) {
+          // These annotations have been filtered by
+          // SymfonyTestsListener::startTest(), so we can assume it's safe to
+          // use them here.
+          $expectedDeprecations = $annotations['method']['expectedDeprecation'];
+        }
+        if ($expectedDeprecations) {
+          restore_error_handler();
+          $test->getTestResultObject()->addError($test, new \PHPUnit_Framework_RiskyTestError('Process-isolated tests might yield a false pass for @expectedDeprecation.'), 0);
+        }
+        return;
+      }
+    }
+    parent::endTest($test, $time);
+  }
+
+  /**
+   * Determines if a test is expected to be run in isolation.
+   *
+   * @param \PHPUnit_Framework_Test $test
+   *   The test in question.
+   *
+   * @return bool
+   *   TRUE if we expect this test to run in isolation, FALSE otherwise.
+   */
+  protected function wouldRunInIsolation(\PHPUnit_Framework_Test $test) {
+    if (!function_exists('__phpunit_run_isolated_test')) {
+      if ($test instanceof \PHPUnit_Framework_TestCase) {
+        $ref_isolated = new \ReflectionProperty($test, 'runTestInSeparateProcess');
+        $ref_isolated->setAccessible(TRUE);
+        return $ref_isolated->getValue($test);
+      }
+    }
+    return FALSE;
+  }
+
+}
