diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index 6670e5acc0be04963f33bdf3bcbc48ee63b9e678..0e0ea2afd4006d6c66b5906ccdbeb864284c27b9 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -661,6 +661,10 @@ function simpletest_clean_environment() {
   else {
     drupal_set_message(t('Clear results is disabled and the test results table will not be cleared.'), 'warning');
   }
+
+  // Detect test classes that have been added, renamed or deleted.
+  \Drupal::cache()->delete('simpletest');
+  \Drupal::cache()->delete('simpletest_phpunit');
 }
 
 /**
diff --git a/core/modules/simpletest/simpletest.services.yml b/core/modules/simpletest/simpletest.services.yml
index 326cf38f2d371f3093ba82f3a57f40ff3e415fa9..8b645deb246a6ef9088e77d7313e456139dd5e0b 100644
--- a/core/modules/simpletest/simpletest.services.yml
+++ b/core/modules/simpletest/simpletest.services.yml
@@ -1,9 +1,4 @@
 services:
   test_discovery:
     class: Drupal\simpletest\TestDiscovery
-    arguments: ['@app.root', '@class_loader', '@module_handler']
-  cache_context.test_discovery:
-    class: Drupal\simpletest\Cache\Context\TestDiscoveryCacheContext
-    arguments: ['@test_discovery', '@private_key']
-    tags:
-      - { name: cache.context}
+    arguments: ['@app.root', '@class_loader', '@module_handler', '@?cache.discovery']
diff --git a/core/modules/simpletest/src/Cache/Context/TestDiscoveryCacheContext.php b/core/modules/simpletest/src/Cache/Context/TestDiscoveryCacheContext.php
deleted file mode 100644
index e3d5cf351d62b47326afc790282ec25fd1ebc56f..0000000000000000000000000000000000000000
--- a/core/modules/simpletest/src/Cache/Context/TestDiscoveryCacheContext.php
+++ /dev/null
@@ -1,94 +0,0 @@
-<?php
-
-namespace Drupal\simpletest\Cache\Context;
-
-use Drupal\Core\Cache\CacheableMetadata;
-use Drupal\Core\Cache\Context\CacheContextInterface;
-use Drupal\Core\PrivateKey;
-use Drupal\Core\Site\Settings;
-use Drupal\simpletest\TestDiscovery;
-
-/**
- * Defines the TestDiscoveryCacheContext service.
- *
- * Cache context ID: 'test_discovery'.
- */
-class TestDiscoveryCacheContext implements CacheContextInterface {
-
-  /**
-   * The test discovery service.
-   *
-   * @var \Drupal\simpletest\TestDiscovery
-   */
-  protected $testDiscovery;
-
-  /**
-   * The private key service.
-   *
-   * @var \Drupal\Core\PrivateKey
-   */
-  protected $privateKey;
-
-  /**
-   * The hash of discovered test information.
-   *
-   * Services should not be stateful, but we only keep this information per
-   * request. That way we don't perform a file scan every time we need this
-   * hash. The test scan results are unlikely to change during the request.
-   *
-   * @var string
-   */
-  protected $hash;
-
-  /**
-   * Construct a test discovery cache context.
-   *
-   * @param \Drupal\simpletest\TestDiscovery $test_discovery
-   *   The test discovery service.
-   * @param \Drupal\Core\PrivateKey $private_key
-   *   The private key service.
-   */
-  public function __construct(TestDiscovery $test_discovery, PrivateKey $private_key) {
-    $this->testDiscovery = $test_discovery;
-    $this->privateKey = $private_key;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getLabel() {
-    return t('Test discovery');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getContext() {
-    if (empty($this->hash)) {
-      $tests = $this->testDiscovery->getTestClasses();
-      $this->hash = $this->hash(serialize($tests));
-    }
-    return $this->hash;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCacheableMetadata() {
-    return new CacheableMetadata();
-  }
-
-  /**
-   * Hashes the given string.
-   *
-   * @param string $identifier
-   *   The string to be hashed.
-   *
-   * @return string
-   *   The hash.
-   */
-  protected function hash($identifier) {
-    return hash('sha256', $this->privateKey->get() . Settings::getHashSalt() . $identifier);
-  }
-
-}
diff --git a/core/modules/simpletest/src/Form/SimpletestTestForm.php b/core/modules/simpletest/src/Form/SimpletestTestForm.php
index 69f31e4572eb8c394ef378370f0cb563c3e6ebc9..0b704f90c54310afed47bde78ddef7c924995d38 100644
--- a/core/modules/simpletest/src/Form/SimpletestTestForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestTestForm.php
@@ -110,10 +110,6 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     ];
 
     $form['tests'] = [
-      '#cache' => [
-        'keys' => ['simpletest_ui_table'],
-        'contexts' => ['test_discovery'],
-      ],
       '#type' => 'table',
       '#id' => 'simpletest-form-table',
       '#tableselect' => TRUE,
diff --git a/core/modules/simpletest/src/TestDiscovery.php b/core/modules/simpletest/src/TestDiscovery.php
index da3d3af4af4feb1c2cc94b7b861686f1729fc018..9e6c32c327cffd3102d0ebf1b0d5a724be5c8c22 100644
--- a/core/modules/simpletest/src/TestDiscovery.php
+++ b/core/modules/simpletest/src/TestDiscovery.php
@@ -6,6 +6,7 @@
 use Doctrine\Common\Reflection\StaticReflectionParser;
 use Drupal\Component\Annotation\Reflection\MockFileFinder;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ExtensionDiscovery;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\simpletest\Exception\MissingGroupException;
@@ -24,11 +25,11 @@ class TestDiscovery {
   protected $classLoader;
 
   /**
-   * Statically cached list of test classes.
+   * Backend for caching discovery results.
    *
-   * @var array
+   * @var \Drupal\Core\Cache\CacheBackendInterface
    */
-  protected $testClasses;
+  protected $cacheBackend;
 
   /**
    * Cached map of all test namespaces to respective directories.
@@ -69,11 +70,14 @@ class TestDiscovery {
    *   \Symfony\Component\ClassLoader\ApcClassLoader.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   (optional) Backend for caching discovery results.
    */
-  public function __construct($root, $class_loader, ModuleHandlerInterface $module_handler) {
+  public function __construct($root, $class_loader, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend = NULL) {
     $this->root = $root;
     $this->classLoader = $class_loader;
     $this->moduleHandler = $module_handler;
+    $this->cacheBackend = $cache_backend;
   }
 
   /**
@@ -155,9 +159,9 @@ public function getTestClasses($extension = NULL, array $types = []) {
     $reader = new SimpleAnnotationReader();
     $reader->addNamespace('Drupal\\simpletest\\Annotation');
 
-    if (!isset($extension) && empty($types)) {
-      if (!empty($this->testClasses)) {
-        return $this->testClasses;
+    if (!isset($extension)) {
+      if ($this->cacheBackend && $cache = $this->cacheBackend->get('simpletest:discovery:classes')) {
+        return $cache->data;
       }
     }
     $list = [];
@@ -211,8 +215,10 @@ public function getTestClasses($extension = NULL, array $types = []) {
     // Allow modules extending core tests to disable originals.
     $this->moduleHandler->alter('simpletest', $list);
 
-    if (!isset($extension) && empty($types)) {
-      $this->testClasses = $list;
+    if (!isset($extension)) {
+      if ($this->cacheBackend) {
+        $this->cacheBackend->set('simpletest:discovery:classes', $list);
+      }
     }
 
     if ($types) {
diff --git a/core/modules/simpletest/tests/src/Kernel/Cache/Context/TestDiscoveryCacheContextTest.php b/core/modules/simpletest/tests/src/Kernel/Cache/Context/TestDiscoveryCacheContextTest.php
deleted file mode 100644
index 25b2af92e4398aba6c4f7ba6fa96994ccb990409..0000000000000000000000000000000000000000
--- a/core/modules/simpletest/tests/src/Kernel/Cache/Context/TestDiscoveryCacheContextTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace Drupal\Tests\simpletest\Kernel\Cache\Context;
-
-use Drupal\KernelTests\KernelTestBase;
-use Drupal\simpletest\Cache\Context\TestDiscoveryCacheContext;
-use Drupal\simpletest\TestDiscovery;
-
-/**
- * @group simpletest
- */
-class TestDiscoveryCacheContextTest extends KernelTestBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public static $modules = ['simpletest'];
-
-  /**
-   * Tests that test context hashes are unique.
-   */
-  public function testContext() {
-    // Mock test discovery.
-    $discovery = $this->getMockBuilder(TestDiscovery::class)
-      ->setMethods(['getTestClasses'])
-      ->disableOriginalConstructor()
-      ->getMock();
-    // Set getTestClasses() to return different results on subsequent calls.
-    // This emulates changed tests in the filesystem.
-    $discovery->expects($this->any())
-      ->method('getTestClasses')
-      ->willReturnOnConsecutiveCalls(
-        ['group1' => ['Test']],
-        ['group2' => ['Test2']]
-      );
-
-    // Make our cache context object.
-    $cache_context = new TestDiscoveryCacheContext($discovery, $this->container->get('private_key'));
-
-    // Generate a context hash.
-    $context_hash = $cache_context->getContext();
-
-    // Since the context stores the hash, we have to reset it.
-    $hash_ref = new \ReflectionProperty($cache_context, 'hash');
-    $hash_ref->setAccessible(TRUE);
-    $hash_ref->setValue($cache_context, NULL);
-
-    // And then assert that we did not generate the same hash for different
-    // content.
-    $this->assertNotSame($context_hash, $cache_context->getContext());
-  }
-
-}