diff --git a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php
index 03990d8e46..82685a9320 100644
--- a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php
+++ b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php
@@ -669,11 +669,11 @@ protected function prepareEnvironment() {
    *   An array of available database driver installer objects.
    */
   protected function getDatabaseTypes() {
-    if ($this->originalContainer) {
+    if (isset($this->originalContainer) && $this->originalContainer) {
       \Drupal::setContainer($this->originalContainer);
     }
     $database_types = drupal_get_database_types();
-    if ($this->originalContainer) {
+    if (isset($this->originalContainer) && $this->originalContainer) {
       \Drupal::unsetContainer();
     }
     return $database_types;
diff --git a/core/scripts/setup-drupal-test.php b/core/scripts/setup-drupal-test.php
new file mode 100644
index 0000000000..304e0377c1
--- /dev/null
+++ b/core/scripts/setup-drupal-test.php
@@ -0,0 +1,20 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * @file
+ * A command line application to install drupal for tests.
+ */
+
+use Drupal\Setup\Commands\TestInstallationSetupApplication;
+
+if (PHP_SAPI !== 'cli') {
+  return;
+}
+
+// Bootstrap.
+$autoloader = require __DIR__ . '/../../autoload.php';
+require_once __DIR__ . '/../tests/bootstrap.php';
+
+$app = new TestInstallationSetupApplication($autoloader);
+$app->run();
diff --git a/core/tests/Drupal/Setup/Commands/TestInstallationSetupApplication.php b/core/tests/Drupal/Setup/Commands/TestInstallationSetupApplication.php
new file mode 100644
index 0000000000..868933f6e6
--- /dev/null
+++ b/core/tests/Drupal/Setup/Commands/TestInstallationSetupApplication.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Drupal\Setup\Commands;
+
+use Symfony\Component\Console\Application;
+
+/**
+ * Application wrapper for TestInstallationSetupCommand.
+ *
+ * In order to see what commands are available and how to use them run
+ * "php core/scripts/setup-drupal-test.php" from command line and use the help
+ * system.
+ *
+ * @internal
+ */
+class TestInstallationSetupApplication extends Application {
+
+  /**
+   * The used PHP autoloader.
+   *
+   * @var object
+   */
+  protected $autoloader;
+
+  /**
+   * SetupDrupalApplication constructor.
+   *
+   * @param string $autoloader
+   *   The used PHP autoloader.
+   */
+  public function __construct($autoloader) {
+    $this->autoloader = $autoloader;
+    parent::__construct('setup-drupal-test', '0.0.1');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getDefaultCommands() {
+    // Even though this is a single command, keep the HelpCommand (--help).
+    $default_commands = parent::getDefaultCommands();
+    $default_commands[] = new TestInstallationSetupCommand();
+    $default_commands[] = new TestTearDownCommand($this->autoloader);
+    return $default_commands;
+  }
+
+}
diff --git a/core/tests/Drupal/Setup/Commands/TestInstallationSetupCommand.php b/core/tests/Drupal/Setup/Commands/TestInstallationSetupCommand.php
new file mode 100644
index 0000000000..47bc6ce465
--- /dev/null
+++ b/core/tests/Drupal/Setup/Commands/TestInstallationSetupCommand.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace Drupal\Setup\Commands;
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\Test\FunctionalTestSetupTrait;
+use Drupal\Core\Test\TestSetupTrait;
+use Drupal\Setup\TestSetupInterface;
+use Drupal\Tests\RandomGeneratorTrait;
+use Drupal\Tests\SessionTestTrait;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Command to create a test Drupal site.
+ *
+ * @internal
+ */
+class TestInstallationSetupCommand extends Command {
+
+  use FunctionalTestSetupTrait {
+    installParameters as protected installParametersTrait;
+  }
+  use RandomGeneratorTrait;
+  use SessionTestTrait;
+  use TestSetupTrait {
+    changeDatabasePrefix as protected changeDatabasePrefixTrait;
+  }
+
+  /**
+   * The install profile to use.
+   *
+   * @var string
+   */
+  protected $profile;
+
+  /**
+   * Time limit in seconds for the test.
+   *
+   * @var int
+   */
+  protected $timeLimit = 500;
+
+  /**
+   * The database prefix of this test run.
+   *
+   * @var string
+   */
+  protected $databasePrefix;
+
+
+  /**
+   * The language to install the site in.
+   *
+   * @var string
+   */
+  protected $langcode = 'en';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    $this->setName('setup-drupal-test')
+      ->setDescription('Creates a test Drupal site')
+      ->setHelp('The details to connect to the test site created will be returned in JSON. It will contain the database prefix and the user agent.')
+      ->addOption('setup_class', NULL, InputOption::VALUE_OPTIONAL, 'A PHP class to setup configuration used by the test, for example, \Drupal\Setup\SetupDrupalTestScript')
+      ->addOption('db_url', NULL, InputOption::VALUE_OPTIONAL, 'URL for database or SIMPLETEST_DB', getenv('SIMPLETEST_DB'))
+      ->addOption('base_url', NULL, InputOption::VALUE_OPTIONAL, 'Base URL for site under test or SIMPLETEST_BASE_URL', getenv('SIMPLETEST_BASE_URL'))
+      ->addOption('install_profile', NULL, InputOption::VALUE_OPTIONAL, 'Install profile to install the site in. Defaults to testing', 'testing')
+      ->addOption('langcode', NULL, InputOption::VALUE_OPTIONAL, 'The language to install the site in.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    // Validate the setup class prior to installing a database to avoid creating
+    // unnecessary sites.
+    $this->validateSetupClass($input->getOption('setup_class'));
+    $db_url = $input->getOption('db_url');
+    $base_url = $input->getOption('base_url');
+    putenv("SIMPLETEST_DB=$db_url");
+    putenv("SIMPLETEST_BASE_URL=$base_url");
+
+    // Manage site fixture.
+    $this->setup($input->getOption('install_profile'), $input->getOption('setup_class'), $input->getOption('langcode'));
+
+    $output->writeln(json_encode([
+      'db_prefix' => $this->databasePrefix,
+      'user_agent' => drupal_generate_test_ua($this->databasePrefix),
+    ]));
+  }
+
+  /**
+   * Validates the setup class.
+   *
+   * @param string|null $class
+   *   The setup class to validate.
+   *
+   * @throws \InvalidArgumentException
+   *   Thrown if the class does not exist or does not implement
+   *   TestSetupInterface.
+   */
+  protected function validateSetupClass($class) {
+    if ($class === NULL) {
+      return;
+    }
+    if (!class_exists($class)) {
+      throw new \InvalidArgumentException("There was a problem loading $class");
+    }
+
+    if (!is_subclass_of($class, TestSetupInterface::class)) {
+      throw new \InvalidArgumentException('You need to define a class implementing \Drupal\Setup\TestSetupInterface');
+    }
+  }
+
+  /**
+   * Creates a test drupal installation.
+   *
+   * @param string $profile
+   *   (optional) The installation profile to use.
+   * @param string $setup_class
+   *   (optional) Setup class. A PHP class to setup configuration used by the
+   *   test.
+   * @param string $langcode
+   *   (optional) The language to install the site in.
+   */
+  public function setup($profile = 'testing', $setup_class = NULL, $langcode = 'en') {
+    $this->profile = $profile;
+    $this->langcode = $langcode;
+    $this->setupBaseUrl();
+    $this->prepareEnvironment();
+    $this->installDrupal();
+
+    if ($setup_class) {
+      $this->executeSetupClass($setup_class);
+    }
+  }
+
+  /**
+   * Installs Drupal into the Simpletest site.
+   */
+  protected function installDrupal() {
+    $this->initUserSession();
+    $this->prepareSettings();
+    $this->doInstall();
+    $this->initSettings();
+    $container = $this->initKernel(\Drupal::request());
+    $this->initConfig($container);
+    $this->installModulesFromClassProperty($container);
+    $this->rebuildAll();
+  }
+
+  /**
+   * Uses the setup file to configure Drupal.
+   *
+   * @param string $class
+   *   The full qualified class name, which should setup Drupal for tests. One
+   *   common need for example would be to create the required content types and
+   *   fields. The class needs to implement \Drupal\Setup\TestSetupInterface
+   *
+   * @see \Drupal\Setup\TestSetupInterface
+   */
+  protected function executeSetupClass($class) {
+    /** @var \Drupal\Setup\TestSetupInterface $instance */
+    $instance = new $class();
+    $instance->setup();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function installParameters() {
+    $parameters = $this->installParametersTrait();
+    $parameters['parameters']['langcode'] = $this->langcode;
+    return $parameters;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function changeDatabasePrefix() {
+    // Ensure that we use the database from SIMPLETEST_DB environment variable.
+    Database::removeConnection('default');
+    $this->changeDatabasePrefixTrait();
+  }
+
+}
diff --git a/core/tests/Drupal/Setup/Commands/TestTearDownCommand.php b/core/tests/Drupal/Setup/Commands/TestTearDownCommand.php
new file mode 100644
index 0000000000..d6ff68b410
--- /dev/null
+++ b/core/tests/Drupal/Setup/Commands/TestTearDownCommand.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace Drupal\Setup\Commands;
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\DrupalKernel;
+use Drupal\Core\Test\TestDatabase;
+use Drupal\Tests\BrowserTestBase;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Command to tear down a test Drupal site.
+ *
+ * @internal
+ */
+class TestTearDownCommand extends Command {
+
+  /**
+   * The used PHP autoloader.
+   *
+   * @var object
+   */
+  protected $autoloader;
+
+  /**
+   * Constructs a new TestTearDownCommand.
+   *
+   * @param string $autoloader
+   *   The used PHP autoloader.
+   * @param string|null $name
+   *   The name of the command. Passing NULL means it must be set in
+   *   configure().
+   */
+  public function __construct($autoloader, $name = NULL) {
+    parent::__construct($name);
+
+    $this->autoloader = $autoloader;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    $this->setName('tear-down-drupal-test')
+      ->setDescription('Removes a test site added by the setup-drupal-test command')
+      ->setHelp('All the database tables and files will be removed.')
+      ->addArgument('db_prefix', InputArgument::REQUIRED, 'The database prefix for the test site')
+      ->addOption('db_url', NULL, InputOption::VALUE_OPTIONAL, 'URL for database or SIMPLETEST_DB', getenv('SIMPLETEST_DB'))
+      ->addOption('base_url', NULL, InputOption::VALUE_OPTIONAL, 'Base URL for site under test or SIMPLETEST_BASE_URL', getenv('SIMPLETEST_BASE_URL'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    $db_url = $input->getOption('db_url');
+    $db_prefix = $input->getArgument('db_prefix');
+    $base_url = $input->getOption('base_url');
+    putenv("SIMPLETEST_DB=$db_url");
+    putenv("SIMPLETEST_BASE_URL=$base_url");
+
+    $kernel = $this->bootstrapDrupal();
+
+    // Handle the cleanup of the test site.
+    $this->tearDown($db_prefix, $db_url, $kernel->getAppRoot());
+  }
+
+  /**
+   * Removes a given instance by deleting all the database tables and files.
+   *
+   * @param string $db_prefix
+   *   The used database prefix.
+   * @param $db_url
+   *   The database URL.
+   * @param $app_root
+   *   The app root.
+   *
+   * @see \Drupal\Tests\BrowserTestBase::cleanupEnvironment()
+   */
+  public function tearDown($db_prefix, $db_url, $app_root) {
+    // Connect to the test database.
+    $database = Database::convertDbUrlToConnectionInfo($db_url, $app_root);
+    $database['prefix'] = ['default' => $db_prefix];
+    Database::addConnectionInfo(__CLASS__, 'default', $database);
+
+    // Remove all the tables.
+    $connection = Database::getConnection('default', __CLASS__);
+    $tables = $connection->schema()->findTables('%');
+    foreach ($tables as $table) {
+      if ($connection->schema()->dropTable($table)) {
+        unset($tables[$table]);
+      }
+    }
+
+    // Delete test site directory.
+    $test_database = new TestDatabase($db_prefix);
+    file_unmanaged_delete_recursive($test_database->getTestSitePath(), [BrowserTestBase::class, 'filePreDeleteCallback']);
+  }
+
+  /**
+   * Bootstraps the drupal kernel.
+   *
+   * @return \Drupal\Core\DrupalKernel
+   *   The Drupal kernel.
+   */
+  protected function bootstrapDrupal() {
+    $request = Request::createFromGlobals();
+    $kernel = DrupalKernel::createFromRequest($request, $this->autoloader, $this->getApplication()->getName());
+
+    // Finish booting Drupal in order to use functions like
+    // file_unmanaged_delete_recursive().
+    $kernel->boot();
+    $kernel->loadLegacyIncludes();
+    return $kernel;
+  }
+
+}
diff --git a/core/tests/Drupal/Setup/SetupDrupalTestScript.php b/core/tests/Drupal/Setup/SetupDrupalTestScript.php
new file mode 100644
index 0000000000..47dd6fb9ed
--- /dev/null
+++ b/core/tests/Drupal/Setup/SetupDrupalTestScript.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Drupal\Setup;
+
+/**
+ * Setup file used by SetupDrupalTestScriptTest.
+ *
+ * @see \Drupal\Tests\Setup\Commands\SetupDrupalTestScriptTest
+ */
+class SetupDrupalTestScript implements TestSetupInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setup() {
+    \Drupal::service('module_installer')->install(['test_page_test']);
+  }
+
+}
diff --git a/core/tests/Drupal/Setup/TestSetupInterface.php b/core/tests/Drupal/Setup/TestSetupInterface.php
new file mode 100644
index 0000000000..3d092839ca
--- /dev/null
+++ b/core/tests/Drupal/Setup/TestSetupInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\Setup;
+
+/**
+ * Allows you to setup an environment used for javascript tests.
+ */
+interface TestSetupInterface {
+
+  /**
+   * Run the code to setup the test environment.
+   *
+   * You have access to any API provided by any installed module. For example,
+   * to install modules use:
+   * @code
+   * \Drupal::service('module_installer')->install(['my_module'])
+   * @endcode
+   *
+   * Check out SetupDrupalTestScript for an example.
+   *
+   * @see \Drupal\Setup\SetupDrupalTestScript
+   */
+  public function setup();
+
+}
diff --git a/core/tests/Drupal/Tests/Setup/Commands/SetupDrupalTestScriptTest.php b/core/tests/Drupal/Tests/Setup/Commands/SetupDrupalTestScriptTest.php
new file mode 100644
index 0000000000..39ed32aef4
--- /dev/null
+++ b/core/tests/Drupal/Tests/Setup/Commands/SetupDrupalTestScriptTest.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace Drupal\Tests\Setup\Commands;
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\Test\TestDatabase;
+use Drupal\Setup\SetupDrupalTestScript;
+use Drupal\Tests\UnitTestCase;
+use GuzzleHttp\Client;
+use GuzzleHttp\Psr7\Request;
+use Symfony\Component\Process\PhpExecutableFinder;
+use Symfony\Component\Process\Process;
+
+/**
+ * Tests setup-drupal-test.php.
+ *
+ * @group Setup
+ *
+ * This test uses the Drupal\Core\Database\Database class which has a static.
+ * Therefore run in an separate process to avoid side effects.
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ *
+ * @see \Drupal\Setup\Commands\TestInstallationSetupApplication
+ * @see \Drupal\Setup\Commands\TestInstallationSetupCommand
+ * @see \Drupal\Setup\Commands\TestTearDownCommand
+ */
+class SetupDrupalTestScriptTest extends UnitTestCase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
+  }
+
+  /**
+   * @coversNothing
+   */
+  public function testInstallWithNonExistingClass() {
+    $phpBinaryFinder = new PhpExecutableFinder();
+    $phpBinaryPath = $phpBinaryFinder->find();
+
+    // Create a connection to the DB configured in SIMPLETEST_DB.
+    $connection = Database::getConnection('default', $this->addTestDatabase(''));
+    $table_count = count($connection->schema()->findTables('%'));
+
+    $command_line = $phpBinaryPath . ' core/scripts/setup-drupal-test.php setup-drupal-test --setup_class "this-class-does-not-exist" --db_url "' . getenv('SIMPLETEST_DB') . '"';
+    $process = new Process($command_line, $this->root);
+    $process->run();
+
+    $this->assertContains('There was a problem loading this-class-does-not-exist', $process->getErrorOutput());
+    $this->assertSame(1, $process->getExitCode());
+    $this->assertSame($table_count, count($connection->schema()->findTables('%')), 'No additional tables created in the database');
+  }
+
+  /**
+   * @coversNothing
+   */
+  public function testInstallWithNonSetupClass() {
+    $phpBinaryFinder = new PhpExecutableFinder();
+    $phpBinaryPath = $phpBinaryFinder->find();
+
+    // Create a connection to the DB configured in SIMPLETEST_DB.
+    $connection = Database::getConnection('default', $this->addTestDatabase(''));
+    $table_count = count($connection->schema()->findTables('%'));
+
+    $command_line = $phpBinaryPath . ' core/scripts/setup-drupal-test.php setup-drupal-test --setup_class "' . static::class . '" --db_url "' . getenv('SIMPLETEST_DB') . '"';
+    $process = new Process($command_line, $this->root);
+    $process->run();
+
+    $this->assertContains('You need to define a class implementing \Drupal\Setup\TestSetupInterface', $process->getErrorOutput());
+    $this->assertSame(1, $process->getExitCode());
+    $this->assertSame($table_count, count($connection->schema()->findTables('%')), 'No additional tables created in the database');
+  }
+
+  /**
+   * @coversNothing
+   */
+  public function testInstallScript() {
+    $phpBinaryFinder = new PhpExecutableFinder();
+    $phpBinaryPath = $phpBinaryFinder->find();
+
+    $command_line = $phpBinaryPath . ' core/scripts/setup-drupal-test.php setup-drupal-test --setup_class "' . SetupDrupalTestScript::class . '" --db_url "' . getenv('SIMPLETEST_DB') . '"';
+    $process = new Process($command_line, $this->root);
+    $process->setTimeout(500);
+    $process->run();
+
+    $this->assertSame(0, $process->getExitCode());
+    $result = json_decode($process->getOutput(), TRUE);
+    $db_prefix = $result['db_prefix'];
+    $this->assertStringStartsWith('simpletest' . substr($db_prefix, 4) . ':', $result['user_agent']);
+
+    $http_client = new Client();
+    $request = (new Request('GET', getenv('SIMPLETEST_BASE_URL') . '/test-page'))
+      ->withHeader('User-Agent', trim($result['user_agent']));
+
+    $response = $http_client->send($request);
+    // Ensure the test_page_test module got installed.
+    $this->assertContains('Test page | Drupal', (string) $response->getBody());
+
+    // Ensure that there are files and database tables for tear down command to
+    // clean up.
+    $key = $this->addTestDatabase($db_prefix);
+    $this->assertGreaterThan(0, count(Database::getConnection('default', $key)->schema()->findTables('%')));
+    $test_database = new TestDatabase($db_prefix);
+    $test_file = $this->root . DIRECTORY_SEPARATOR . $test_database->getTestSitePath() . DIRECTORY_SEPARATOR . '.htkey';
+    $this->assertFileExists($test_file);
+
+    // Install another site so we can ensure tear down only removes one site at
+    // a time.
+    $process->run();
+    $result = json_decode($process->getOutput(), TRUE);
+    $other_db_prefix = $result['db_prefix'];
+    $other_key = $this->addTestDatabase($other_db_prefix);
+    $this->assertGreaterThan(0, count(Database::getConnection('default', $other_key)->schema()->findTables('%')));
+
+    // Now test the tear down process as well.
+    $command_line = $phpBinaryPath . ' core/scripts/setup-drupal-test.php tear-down-drupal-test ' . $db_prefix . ' --db_url "' . getenv('SIMPLETEST_DB') . '"';
+    $process = new Process($command_line, $this->root);
+    $process->setTimeout(500);
+    $process->run();
+    $this->assertSame(0, $process->getExitCode());
+
+    // Ensure that all the tables and files for this DB prefix are gone.
+    $this->assertCount(0, Database::getConnection('default', $key)->schema()->findTables('%'));
+    $this->assertFileNotExists($test_file);
+
+    // Ensure the other sites tables and files still exist.
+    $this->assertGreaterThan(0, count(Database::getConnection('default', $other_key)->schema()->findTables('%')));
+    $test_database = new TestDatabase($other_db_prefix);
+    $test_file = $this->root . DIRECTORY_SEPARATOR . $test_database->getTestSitePath() . DIRECTORY_SEPARATOR . '.htkey';
+    $this->assertFileExists($test_file);
+
+    // Now test the tear down process as well.
+    $command_line = $phpBinaryPath . ' core/scripts/setup-drupal-test.php tear-down-drupal-test ' . $other_db_prefix . ' --db_url "' . getenv('SIMPLETEST_DB') . '"';
+    $process = new Process($command_line, $this->root);
+    $process->setTimeout(500);
+    $process->run();
+    $this->assertSame(0, $process->getExitCode());
+
+    $this->assertCount(0, Database::getConnection('default', $other_key)->schema()->findTables('%'));
+    $this->assertFileNotExists($test_file);
+  }
+
+  /**
+   * @coversNothing
+   */
+  public function testInstallInDifferentLanguage() {
+    $phpBinaryFinder = new PhpExecutableFinder();
+    $phpBinaryPath = $phpBinaryFinder->find();
+
+    $command_line = $phpBinaryPath . ' core/scripts/setup-drupal-test.php setup-drupal-test --langcode fr --setup_class "' . SetupDrupalTestScript::class . '" --db_url "' . getenv('SIMPLETEST_DB') . '"';
+    $process = new Process($command_line, $this->root);
+    $process->setTimeout(500);
+    $process->run();
+    $this->assertEquals(0, $process->getExitCode());
+
+    $result = json_decode($process->getOutput(), TRUE);
+    $db_prefix = $result['db_prefix'];
+    $http_client = new Client();
+    $request = (new Request('GET', getenv('SIMPLETEST_BASE_URL') . '/test-page'))
+      ->withHeader('User-Agent', trim($result['user_agent']));
+
+    $response = $http_client->send($request);
+    // Ensure the test_page_test module got installed.
+    $this->assertContains('Test page | Drupal', (string) $response->getBody());
+    $this->assertContains('lang="fr"', (string) $response->getBody());
+
+    // Now test the tear down process as well.
+    $command_line = $phpBinaryPath . ' core/scripts/setup-drupal-test.php tear-down-drupal-test ' . $db_prefix . ' --db_url "' . getenv('SIMPLETEST_DB') . '"';
+    $process = new Process($command_line, $this->root);
+    $process->setTimeout(500);
+    $process->run();
+    $this->assertSame(0, $process->getExitCode());
+
+    // Ensure that all the tables for this DB prefix are gone.
+    $this->assertCount(0, Database::getConnection('default', $this->addTestDatabase($db_prefix))->schema()->findTables('%'));
+  }
+
+  /**
+   * Adds the installed test site to the database connection info.
+   *
+   * @param $db_prefix
+   *   The prefix of the installed test site.
+   *
+   * @return string
+   *   The database key of the added connection.
+   */
+  protected function addTestDatabase($db_prefix) {
+    $database = Database::convertDbUrlToConnectionInfo(getenv('SIMPLETEST_DB'), $this->root);
+    $database['prefix'] = ['default' => $db_prefix];
+    $target = __CLASS__ . $db_prefix;
+    Database::addConnectionInfo($target, 'default', $database);
+    return $target;
+  }
+
+}
diff --git a/core/tests/bootstrap.php b/core/tests/bootstrap.php
index 7eb6ecb70c..adcf5b169a 100644
--- a/core/tests/bootstrap.php
+++ b/core/tests/bootstrap.php
@@ -129,6 +129,7 @@ function drupal_phpunit_populate_class_loader() {
 
   // Start with classes in known locations.
   $loader->add('Drupal\\Tests', __DIR__);
+  $loader->add('Drupal\\Setup', __DIR__);
   $loader->add('Drupal\\KernelTests', __DIR__);
   $loader->add('Drupal\\FunctionalTests', __DIR__);
   $loader->add('Drupal\\FunctionalJavascriptTests', __DIR__);
@@ -195,5 +196,5 @@ class_alias('\PHPUnit\Framework\MockObject\Matcher\InvokedRecorder', '\PHPUnit_F
   class_alias('\PHPUnit\Framework\SkippedTestError', '\PHPUnit_Framework_SkippedTestError');
   class_alias('\PHPUnit\Framework\TestCase', '\PHPUnit_Framework_TestCase');
   class_alias('\PHPUnit\Util\Test', '\PHPUnit_Util_Test');
-  class_alias('\PHPUnit\Util\XML', '\PHPUnit_Util_XML');
+  class_alias('\PHPUnit\Util\Xml', '\PHPUnit_Util_XML');
 }
