diff --git a/composer.lock b/composer.lock
index ddaa66ee82..be64d24bdf 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4194,23 +4194,23 @@
         },
         {
             "name": "symfony/phpunit-bridge",
-            "version": "v3.2.8",
+            "version": "dev-master",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/phpunit-bridge.git",
-                "reference": "00916603c524b8048906de460b7ea0dfa1651281"
+                "reference": "884e6aac4e6a07d427187a35194821cbc2a0c82c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/00916603c524b8048906de460b7ea0dfa1651281",
-                "reference": "00916603c524b8048906de460b7ea0dfa1651281",
+                "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/884e6aac4e6a07d427187a35194821cbc2a0c82c",
+                "reference": "884e6aac4e6a07d427187a35194821cbc2a0c82c",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3"
             },
             "conflict": {
-                "phpunit/phpunit": ">=6.0"
+                "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0"
             },
             "suggest": {
                 "ext-zip": "Zip support is required when using bin/simple-phpunit",
@@ -4222,7 +4222,7 @@
             "type": "symfony-bridge",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "4.0-dev"
                 }
             },
             "autoload": {
@@ -4252,13 +4252,14 @@
             ],
             "description": "Symfony PHPUnit Bridge",
             "homepage": "https://symfony.com",
-            "time": "2017-04-12T14:13:17+00:00"
+            "time": "2017-10-13T14:15:21+00:00"
         }
     ],
     "aliases": [],
     "minimum-stability": "dev",
     "stability-flags": {
-        "behat/mink": 20
+        "behat/mink": 20,
+        "symfony/phpunit-bridge": 20
     },
     "prefer-stable": true,
     "prefer-lowest": false,
diff --git a/core/composer.json b/core/composer.json
index c802e5d6ee..a7f20eac65 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -47,7 +47,7 @@
         "phpunit/phpunit": ">=4.8.35 <5",
         "phpspec/prophecy": "^1.4",
         "symfony/css-selector": "~3.2.8",
-        "symfony/phpunit-bridge": "~3.2.8"
+        "symfony/phpunit-bridge": "dev-master"
     },
     "replace": {
         "drupal/action": "self.version",
diff --git a/core/includes/errors.inc b/core/includes/errors.inc
index 284d1835e9..e3fd2256c1 100644
--- a/core/includes/errors.inc
+++ b/core/includes/errors.inc
@@ -83,6 +83,19 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line, $c
       '@backtrace_string' => (new \Exception())->getTraceAsString(),
     ], $recoverable || $to_string);
   }
+  // If the site is a test site then fail for user deprecations so they can be
+  // caught by the deprecation error handler.
+  elseif (DRUPAL_TEST_IN_CHILD_SITE && $error_level === E_USER_DEPRECATED) {
+    $backtrace = debug_backtrace();
+    $caller = Error::getLastCaller($backtrace);
+    _drupal_error_header(
+      Markup::create(Xss::filterAdmin($message)),
+      'User deprecated function',
+      $caller['function'],
+      $caller['file'],
+      $caller['line']
+    );
+  }
 }
 
 /**
@@ -136,25 +149,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
   // When running inside the testing framework, we relay the errors
   // to the tested site by the way of HTTP headers.
   if (DRUPAL_TEST_IN_CHILD_SITE && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
-    // $number does not use drupal_static as it should not be reset
-    // as it uniquely identifies each PHP error.
-    static $number = 0;
-    $assertion = [
-      $error['@message'],
-      $error['%type'],
-      [
-        'function' => $error['%function'],
-        'file' => $error['%file'],
-        'line' => $error['%line'],
-      ],
-    ];
-    // For non-fatal errors (e.g. PHP notices) _drupal_log_error can be called
-    // multiple times per request. In that case the response is typically
-    // generated outside of the error handler, e.g., in a controller. As a
-    // result it is not possible to use a Response object here but instead the
-    // headers need to be emitted directly.
-    header('X-Drupal-Assertion-' . $number . ': ' . rawurlencode(serialize($assertion)));
-    $number++;
+    _drupal_error_header($error['@message'], $error['%type'], $error['%function'], $error['%file'], $error['%line']);
   }
 
   $response = new Response();
@@ -325,3 +320,39 @@ function _drupal_get_error_level() {
   // request on a public site, so use the non-verbose default value.
   return $error_level ?: ERROR_REPORTING_DISPLAY_ALL;
 }
+
+/**
+ * Adds error information to headers so that tests can access it.
+ *
+ * @param $message
+ *   The error message.
+ * @param $type
+ *   The type of error.
+ * @param $function
+ *   The function that emitted the error.
+ * @param $file
+ *   The file that emitted the error.
+ * @param $line
+ *   The line number in file that emitted the error.
+ */
+function _drupal_error_header($message, $type, $function, $file, $line) {
+  // $number does not use drupal_static as it should not be reset
+  // as it uniquely identifies each PHP error.
+  static $number = 0;
+  $assertion = [
+    $message,
+    $type,
+    [
+      'function' => $function,
+      'file' => $file,
+      'line' => $line,
+    ],
+  ];
+  // For non-fatal errors (e.g. PHP notices) _drupal_log_error can be called
+  // multiple times per request. In that case the response is typically
+  // generated outside of the error handler, e.g., in a controller. As a
+  // result it is not possible to use a Response object here but instead the
+  // headers need to be emitted directly.
+  header('X-Drupal-Assertion-' . $number . ': ' . rawurlencode(serialize($assertion)));
+  $number++;
+}
diff --git a/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php b/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php
index 3c4d1427a8..8c01cc8ecb 100644
--- a/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php
+++ b/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php
@@ -11,6 +11,12 @@
  */
 class TestHttpClientMiddleware {
 
+  protected $skippedDeprecations = [
+    'As of 3.1 an Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface is used to resolve arguments. In 4.0 the $argumentResolver becomes the Symfony\Component\HttpKernel\Controller\ArgumentResolver if no other is provided instead of using the $resolver argument.',
+    'Symfony\Component\HttpKernel\Controller\ControllerResolver::getArguments is deprecated as of 3.1 and will be removed in 4.0. Implement the Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface and inject it in the HttpKernel instead.',
+    'The Twig_Node::getLine method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateLine() instead.',
+  ];
+
   /**
    * {@inheritdoc}
    *
@@ -41,7 +47,17 @@ public function __invoke() {
                   // the header.
                   $parameters = unserialize(urldecode($header_value));
                   if (count($parameters) === 3) {
-                    throw new \Exception($parameters[1] . ': ' . $parameters[0] . "\n" . Error::formatBacktrace([$parameters[2]]));
+                    if ($parameters[1] === 'User deprecated function') {
+                      $message = (string) $parameters[0];
+                      if (!in_array($message, $this->skippedDeprecations)) {
+                        // Fire the same deprecation message to allow it to be
+                        // caught.
+                        @trigger_error($message, E_USER_DEPRECATED);
+                      }
+                    }
+                    else {
+                      throw new \Exception($parameters[1] . ': ' . $parameters[0] . "\n" . Error::formatBacktrace([$parameters[2]]));
+                    }
                   }
                   else {
                     throw new \Exception('Error thrown with the wrong amount of parameters.');
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
index fe4ebb8310..20874821af 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -491,10 +491,16 @@ public function testGet() {
     // GET requests - depending on web server configuration. This would usually
     // be 'Transfer-Encoding: chunked'.
     $ignored_headers = ['Date', 'Content-Length', 'X-Drupal-Cache', 'X-Drupal-Dynamic-Cache', 'Transfer-Encoding'];
-    foreach ($ignored_headers as $ignored_header) {
-      unset($head_headers[$ignored_header]);
-      unset($get_headers[$ignored_header]);
-    }
+    $header_cleaner = function ($headers) use ($ignored_headers) {
+      foreach ($headers as $header => $value) {
+        if (strpos($header, 'X-Drupal-Assertion-') === 0 || in_array($header, $ignored_headers)) {
+          unset($headers[$header]);
+        }
+      }
+      return $headers;
+    };
+    $get_headers = $header_cleaner($get_headers);
+    $head_headers = $header_cleaner($head_headers);
     $this->assertSame($get_headers, $head_headers);
 
     // BC: serialization_update_8302().
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index b5874b5ae6..e2b83814d6 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -690,9 +690,14 @@ protected function curlHeaderCallback($curlHandler, $header) {
     // generated by _drupal_log_error() in the exact form required
     // by \Drupal\simpletest\WebTestBase::error().
     if (preg_match('/^X-Drupal-Assertion-[0-9]+: (.*)$/', $header, $matches)) {
-      // Call \Drupal\simpletest\WebTestBase::error() with the parameters from
-      // the header.
-      call_user_func_array([&$this, 'error'], unserialize(urldecode($matches[1])));
+      $parameters = unserialize(urldecode($matches[1]));
+      // Swallow deprecation notices. WebTestBase will not be updated to handle
+      // them.
+      if ($parameters[1] !== 'User deprecated function') {
+        // Call \Drupal\simpletest\WebTestBase::error() with the parameters from
+        // the header.
+        call_user_func_array([&$this, 'error'], $parameters);
+      }
     }
 
     // Save cookies.
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..e255021d65
--- /dev/null
+++ b/core/modules/system/tests/modules/deprecation_test/deprecation_test.module
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Contains functions for testing calling deprecated functions in tests.
+ */
+
+/**
+ * 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/tests/Drupal/FunctionalTests/Core/Test/PhpUnitBridgeTest.php b/core/tests/Drupal/FunctionalTests/Core/Test/PhpUnitBridgeTest.php
new file mode 100644
index 0000000000..bc843c31a1
--- /dev/null
+++ b/core/tests/Drupal/FunctionalTests/Core/Test/PhpUnitBridgeTest.php
@@ -0,0 +1,32 @@
+<?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());
+  }
+
+  /**
+   * @expectedDeprecation This is the deprecation message for deprecation_test_function().
+   */
+  public function testErrorOnSiteUnderTest() {
+    $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..73ba0de167
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Test/PhpUnitBridgeTest.php
@@ -0,0 +1,29 @@
+<?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());
+  }
+
+  public function testDeprecatedFunction() {
+    $this->markTestIncomplete('Modules are not loaded for unit tests, so deprecated_test_function() will not be available.');
+    $this->assertEquals('known_return_value', \deprecation_test_function());
+  }
+
+}
