diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 906a7c1..fd5bf01 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -2471,6 +2471,16 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) {
     // requests.
     $container = new ContainerBuilder();
 
+    // Database.
+    $container->register('database', 'Drupal\Core\Database\Connection')
+      ->setFactoryClass('Drupal\Core\Database\Database')
+      ->setFactoryMethod('getConnection')
+      ->addArgument('default');
+    $container->register('database.slave', 'Drupal\Core\Database\Connection')
+      ->setFactoryClass('Drupal\Core\Database\Database')
+      ->setFactoryMethod('getConnection')
+      ->addArgument('slave');
+
     // Register active configuration storage.
     $container
       ->register('config.cachedstorage.storage', 'Drupal\Core\Config\FileStorage')
diff --git a/core/includes/cache.inc b/core/includes/cache.inc
index 06b0ab1..72339b0 100644
--- a/core/includes/cache.inc
+++ b/core/includes/cache.inc
@@ -26,13 +26,12 @@
  * @see Drupal\Core\Cache\CacheBackendInterface
  */
 function cache($bin = 'cache') {
+  $cache_objects = &drupal_static(__FUNCTION__, array());
+
   // Temporary backwards compatibiltiy layer, allow old style prefixed cache
   // bin names to be passed as arguments.
   $bin = str_replace('cache_', '', $bin);
 
-  // We do not use drupal_static() here because we do not want to change the
-  // storage of a cache bin mid-request.
-  static $cache_objects;
   if (!isset($cache_objects[$bin])) {
     $cache_objects[$bin] = CacheFactory::get($bin);
   }
diff --git a/core/includes/config.inc b/core/includes/config.inc
index 3ffcaec..3ba23a1 100644
--- a/core/includes/config.inc
+++ b/core/includes/config.inc
@@ -157,8 +157,6 @@ function config_import() {
   try {
     $remaining_changes = config_import_invoke_owner($config_changes, $source_storage, $target_storage);
     config_sync_changes($remaining_changes, $source_storage, $target_storage);
-    // Flush all caches and reset static variables after a successful import.
-    drupal_flush_all_caches();
   }
   catch (ConfigException $e) {
     watchdog_exception('config_import', $e);
diff --git a/core/includes/module.inc b/core/includes/module.inc
index cbdbeaf..e7baf92 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -1075,8 +1075,10 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
     global $theme, $base_theme_info;
     if (isset($theme)) {
       $theme_keys = array();
-      foreach ($base_theme_info as $base) {
-        $theme_keys[] = $base->name;
+      if (isset($base_theme_info)) {
+        foreach ($base_theme_info as $base) {
+          $theme_keys[] = $base->name;
+        }
       }
       $theme_keys[] = $theme;
       foreach ($theme_keys as $theme_key) {
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index 2c5d180..cc0d32b 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -43,14 +43,6 @@ public function build(ContainerBuilder $container) {
     $container->register('language_manager', 'Drupal\Core\Language\LanguageManager')
       ->addArgument(new Reference('request'))
       ->setScope('request');
-    $container->register('database', 'Drupal\Core\Database\Connection')
-      ->setFactoryClass('Drupal\Core\Database\Database')
-      ->setFactoryMethod('getConnection')
-      ->addArgument('default');
-    $container->register('database.slave', 'Drupal\Core\Database\Connection')
-      ->setFactoryClass('Drupal\Core\Database\Database')
-      ->setFactoryMethod('getConnection')
-      ->addArgument('slave');
 
     // @todo Replace below lines with the commented out block below it when it's
     //   performant to do so: http://drupal.org/node/1706064.
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php
index 9eea495..2568c24 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php
@@ -7,12 +7,12 @@
 
 namespace Drupal\config\Tests;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\simpletest\UnitTestBase;
 
 /**
  * Tests CRUD operations on configuration objects.
  */
-class ConfigCRUDTest extends WebTestBase {
+class ConfigCRUDTest extends UnitTestBase {
   public static function getInfo() {
     return array(
       'name' => 'CRUD operations',
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php
index 6b8b033..4a15c10 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php
@@ -8,12 +8,12 @@
 namespace Drupal\config\Tests;
 
 use Drupal\Core\Config\FileStorage;
-use Drupal\simpletest\WebTestBase;
+use Drupal\simpletest\UnitTestBase;
 
 /**
  * Tests reading and writing file contents.
  */
-class ConfigFileContentTest extends WebTestBase {
+class ConfigFileContentTest extends UnitTestBase {
   public static function getInfo() {
     return array(
       'name' => 'File content',
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
index 9316962..f0a0e78 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php
@@ -7,12 +7,12 @@
 
 namespace Drupal\config\Tests;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\simpletest\UnitTestBase;
 
 /**
  * Tests importing configuration from files into active store.
  */
-class ConfigImportTest extends WebTestBase {
+class ConfigImportTest extends UnitTestBase {
 
   /**
    * Modules to enable.
@@ -32,7 +32,10 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
 
-    // Clear out any possibly existing hook invocation records.
+    config_install_default_config('module', 'config_test');
+    // Installing config_test's default configuration pollutes the global
+    // variable being used for recording hook invocations by this test already,
+    // so it has to be cleared out manually.
     unset($GLOBALS['hook_config_test']);
   }
 
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
index 3e6a88e..8a3f3a3 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
@@ -7,12 +7,12 @@
 
 namespace Drupal\config\Tests;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\simpletest\UnitTestBase;
 
 /**
  * Tests installation of configuration objects in installation functionality.
  */
-class ConfigInstallTest extends WebTestBase {
+class ConfigInstallTest extends UnitTestBase {
   public static function getInfo() {
     return array(
       'name' => 'Installation functionality',
@@ -21,6 +21,11 @@ public static function getInfo() {
     );
   }
 
+  function setUp() {
+    parent::setUp();
+    $this->installSchema('system', 'system');
+  }
+
   /**
    * Tests module installation.
    */
@@ -35,7 +40,7 @@ function testModuleInstallation() {
     $this->assertIdentical($config->isNew(), TRUE);
 
     // Install the test module.
-    module_enable(array('config_test'));
+    $this->enableModules(array('config_test'));
 
     // Verify that default module config exists.
     $config = config($default_config);
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigOverrideTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigOverrideTest.php
index 4e0d8f7..7cc032a 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigOverrideTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigOverrideTest.php
@@ -7,12 +7,12 @@
 
 namespace Drupal\config\Tests;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\simpletest\UnitTestBase;
 
 /**
  * Tests configuration overrides via $conf in settings.php.
  */
-class ConfigOverrideTest extends WebTestBase {
+class ConfigOverrideTest extends UnitTestBase {
 
   /**
    * Modules to enable.
@@ -29,6 +29,12 @@ public static function getInfo() {
     );
   }
 
+  function setUp() {
+    parent::setUp();
+
+    config_install_default_config('module', 'config_test');
+  }
+
   /**
    * Tests configuration override.
    */
diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
index 6b7a74f..f794500 100644
--- a/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
+++ b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\config\Tests\Storage;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\simpletest\UnitTestBase;
 
 /**
  * Base class for testing storage controller operations.
@@ -21,7 +21,7 @@
  * supply the necessary helper methods to interact with the raw/native storage
  * directly.
  */
-abstract class ConfigStorageTestBase extends WebTestBase {
+abstract class ConfigStorageTestBase extends UnitTestBase {
 
   /**
    * Tests storage controller CRUD operations.
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index 6f17f57..816bffb 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -695,6 +695,20 @@ protected function prepareEnvironment() {
     $this->originalThemeKey = $GLOBALS['theme_key'];
     $this->originalTheme = $GLOBALS['theme'];
 
+    // Back up all current global variables, so as be able to restore them in
+    // TestBase::tearDown(). Without this, global variables of a previous test
+    // case/method would leak into the next test case/method.
+    // PHP opens the door to insanity when trying to use array_* functions (such
+    // as array_diff_key()) on the $GLOBALS superglobal. It is required to loop
+    // over the keys in $GLOBALS manually.
+    $this->originalGlobals = array();
+    $php_globals = $this->listPhpGlobals();
+    foreach ($GLOBALS as $key => $value) {
+      if (!isset($php_globals[$key])) {
+        $this->originalGlobals[$key] = $value;
+      }
+    }
+
     // Save further contextual information.
     $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files');
     $this->originalProfile = drupal_get_profile();
@@ -721,30 +735,36 @@ protected function prepareEnvironment() {
     file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY);
     $this->generatedTestFiles = FALSE;
 
+    // Unset all globals.
+    // @todo Make it possible to unset() all non-PHP globals - or to
+    //   "re-bootstrap" Drupal. The global variables being set up by
+    //   drupal_environment_initialize() and drupal_settings_initialize()
+    //   prevent that currently.
+    //$GLOBALS = array();
+    unset($GLOBALS['theme_key']);
+    unset($GLOBALS['theme']);
+
     // Create and set new configuration directories. The child site
     // uses drupal_valid_test_ua() to adjust the config directory paths to
     // a test-prefix-specific directory within the public files directory.
     // @see config_get_config_directory()
     $GLOBALS['config_directories'] = array();
-    foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) {
-      $GLOBALS['config_directories'][$type] = 'simpletest/' . substr($this->databasePrefix, 10) . '/config_' . $type;
-    }
-
-    // Reset and create a new service container.
-    $this->container = drupal_container(NULL, TRUE);
-
     $this->configDirectories = array();
     include_once DRUPAL_ROOT . '/core/includes/install.inc';
-    foreach ($GLOBALS['config_directories'] as $type => $path) {
+    foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) {
+      // Assign the relative path to the global variable.
+      $path = 'simpletest/' . substr($this->databasePrefix, 10) . '/config_' . $type;
+      $GLOBALS['config_directories'][$type] = $path;
+      // Ensure the directory can be created and is writeable.
       if (!install_ensure_config_directory($type)) {
         return FALSE;
       }
+      // Provide the already resolved path for tests.
       $this->configDirectories[$type] = $this->originalFileDirectory . '/' . $path;
     }
 
-    // Unset globals.
-    unset($GLOBALS['theme_key']);
-    unset($GLOBALS['theme']);
+    // Reset and create a new service container.
+    $this->container = drupal_container(NULL, TRUE);
 
     // Re-initialize the theme to ensure that tests do not see an inconsistent
     // behavior when calling functions that would initialize the theme if it has
@@ -765,6 +785,15 @@ protected function prepareEnvironment() {
   }
 
   /**
+   * Lists reserved keys of PHP $GLOBALS.
+   */
+  private function listPhpGlobals() {
+    return array_flip(array(
+      'GLOBALS', 'argv', 'argc', '_POST', '_GET', '_COOKIE', '_FILES', '_ENV', '_REQUEST', '_SERVER',
+    ));
+  }
+
+  /**
    * Deletes created files, database tables, and reverts all environment changes.
    *
    * This method needs to be invoked for both unit and integration tests.
@@ -777,6 +806,24 @@ protected function tearDown() {
     global $user, $conf;
     $language_interface = language(LANGUAGE_TYPE_INTERFACE);
 
+    // Ensure that TestBase::changeDatabasePrefix() has run and TestBase::$setup
+    // was not tricked into TRUE, since the following code would delete the
+    // entire parent site otherwise.
+    if ($this->setupDatabasePrefix) {
+      // Remove all prefixed tables.
+      $connection_info = Database::getConnectionInfo('default');
+      $tables = db_find_tables($connection_info['default']['prefix']['default'] . '%');
+      $prefix_length = strlen($connection_info['default']['prefix']['default']);
+      foreach ($tables as $table) {
+        if (db_drop_table(substr($table, $prefix_length))) {
+          unset($tables[$table]);
+        }
+      }
+      if (!empty($tables)) {
+        $this->fail('Failed to drop all prefixed tables.');
+      }
+    }
+
     // In case a fatal error occurred that was not in the test process read the
     // log to pick up any fatal errors.
     simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE);
@@ -795,6 +842,20 @@ protected function tearDown() {
     Database::renameConnection('simpletest_original_default', 'default');
 
     // Restore original globals.
+    // First, unset any global variable that is not a reserved PHP global.
+    $php_globals = $this->listPhpGlobals();
+    foreach ($GLOBALS as $key => $value) {
+      if (!isset($php_globals[$key])) {
+        unset($GLOBALS[$key]);
+      }
+    }
+    // Second, re-set all global variables that existed prior to executing the
+    // test.
+    foreach ($this->originalGlobals as $key => $value) {
+      if (!isset($php_globals[$key])) {
+        $GLOBALS[$key] = $value;
+      }
+    }
     $GLOBALS['theme_key'] = $this->originalThemeKey;
     $GLOBALS['theme'] = $this->originalTheme;
 
@@ -830,6 +891,7 @@ protected function tearDown() {
    */
   public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
     if ($severity & error_reporting()) {
+      require_once DRUPAL_ROOT . '/core/includes/errors.inc';
       $error_map = array(
         E_STRICT => 'Run-time notice',
         E_WARNING => 'Warning',
@@ -843,6 +905,14 @@ public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
       );
 
       $backtrace = debug_backtrace();
+
+      // Add verbose backtrace for errors, but not for debug() messages.
+      if ($severity !== E_USER_NOTICE) {
+        $verbose_backtrace = $backtrace;
+        array_shift($verbose_backtrace);
+        $message .= '<pre class="backtrace">' . format_backtrace($verbose_backtrace) . '</pre>';
+      }
+
       $this->error($message, $error_map[$severity], _drupal_get_last_caller($backtrace));
     }
     return TRUE;
@@ -854,15 +924,19 @@ public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
    * @see set_exception_handler
    */
   protected function exceptionHandler($exception) {
+    require_once DRUPAL_ROOT . '/core/includes/errors.inc';
     $backtrace = $exception->getTrace();
+    $verbose_backtrace = $backtrace;
     // Push on top of the backtrace the call that generated the exception.
     array_unshift($backtrace, array(
       'line' => $exception->getLine(),
       'file' => $exception->getFile(),
     ));
-    require_once DRUPAL_ROOT . '/core/includes/errors.inc';
     // The exception message is run through check_plain() by _drupal_decode_exception().
-    $this->error(t('%type: !message in %function (line %line of %file).', _drupal_decode_exception($exception)), 'Uncaught exception', _drupal_get_last_caller($backtrace));
+    $message = format_string('%type: !message in %function (line %line of %file). <pre class="backtrace">!backtrace</pre>', _drupal_decode_exception($exception) + array(
+      '!backtrace' => format_backtrace($verbose_backtrace),
+    ));
+    $this->error($message, 'Uncaught exception', _drupal_get_last_caller($backtrace));
   }
 
   /**
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
index 98ed71d..54a4fe7 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
@@ -20,6 +20,18 @@
 abstract class UnitTestBase extends TestBase {
 
   /**
+   * Fixed module list being used by this test.
+   *
+   * @var array
+   *   An associative array containing the required data for the $fixed_list
+   *   argument of module_list().
+   *
+   * @see UnitTestBase::setUp()
+   * @see UnitTestBase::enableModules()
+   */
+  private $moduleList = array();
+
+  /**
    * Constructor for UnitTestBase.
    */
   function __construct($test_id = NULL) {
@@ -46,16 +58,32 @@ protected function setUp() {
     if (!$this->setupEnvironment) {
       return FALSE;
     }
-    $this->originalThemeRegistry = theme_get_registry(FALSE);
 
     // Reset all statics and variables to perform tests in a clean environment.
     $conf = array();
     drupal_static_reset();
 
-    // Enforce an empty module list.
-    module_list(NULL, array());
+    // Collect and set fixed module list.
+    $class = get_class($this);
+    $modules = array();
+    while ($class) {
+      if (property_exists($class, 'modules')) {
+        $modules = array_merge($modules, $class::$modules);
+      }
+      $class = get_parent_class($class);
+    }
+    foreach ($modules as $module) {
+      $this->moduleList[$module] = drupal_get_path('module', $module);
+    }
+    module_list(NULL, $this->moduleList);
+    module_load_all();
 
+    // Provide a minimal environment for unit tests.
     $conf['file_public_path'] = $this->public_files_directory;
+    $conf['lock_backend'] = 'Drupal\Core\Lock\NullLockBackend';
+    $conf['cache_classes'] = array('cache' => 'Drupal\Core\Cache\NullBackend');
+    $this->container->register('config.storage', 'Drupal\Core\Config\FileStorage')
+      ->addArgument($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
 
     // Change the database prefix.
     // All static variables need to be reset before the database prefix is
@@ -71,4 +99,44 @@ protected function setUp() {
 
     $this->setup = TRUE;
   }
+
+  /**
+   * Installs a specific table from a module schema definition.
+   *
+   * @param string $module
+   *   The name of the module that defines the table's schema.
+   * @param string $table
+   *   The name of the table to install.
+   */
+  protected function installSchema($module, $table) {
+    require_once DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$module.install";
+    $function = $module . '_schema';
+    $schema = $function();
+    Database::getConnection()->schema()->createTable($table, $schema[$table]);
+  }
+
+  /**
+   * Enables modules for this test.
+   *
+   * The {system} table is required for using this method. Install it via:
+   * @code
+   * $this->installSchema('system', 'system');
+   * @endcode
+   *
+   * @param array $modules
+   *   A list of modules to enable.
+   */
+  protected function enableModules(array $modules) {
+    // Add the new modules to the fixed module_list() first, since callbacks
+    // invoked by module_enable() may need to access information provided by
+    // info hooks of the new modules already. module_enable() enables the new
+    // modules in {system}, but that has no effect, since we are operating with
+    // a fixed module list.
+    foreach ($modules as $module) {
+      $this->moduleList[$module] = drupal_get_path('module', $module);
+    }
+    module_list(NULL, $this->moduleList);
+
+    module_enable($modules);
+  }
 }
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 10501d0..758a440 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -752,32 +752,10 @@ protected function refreshVariables() {
    * and reset the database prefix.
    */
   protected function tearDown() {
-    // Ensure that TestBase::changeDatabasePrefix() has run and TestBase::$setup
-    // was not tricked into TRUE, since the following code would delete the
-    // entire parent site otherwise.
-    if (!$this->setupDatabasePrefix) {
-      return FALSE;
-    }
     // Destroy the testing kernel.
     if (isset($this->kernel)) {
       $this->kernel->shutdown();
     }
-    // Remove all prefixed tables.
-    $connection_info = Database::getConnectionInfo('default');
-    $tables = db_find_tables($connection_info['default']['prefix']['default'] . '%');
-    if (empty($tables)) {
-      $this->fail('Failed to find test tables to drop.');
-    }
-    $prefix_length = strlen($connection_info['default']['prefix']['default']);
-    foreach ($tables as $table) {
-      if (db_drop_table(substr($table, $prefix_length))) {
-        unset($tables[$table]);
-      }
-    }
-    if (!empty($tables)) {
-      $this->fail('Failed to drop all prefixed tables.');
-    }
-
     parent::tearDown();
 
     // Ensure that internal logged in variable and cURL options are reset.
