abstract class KernelTestBase in Zircon Profile 8
Same name in this branch
- 8 core/tests/Drupal/KernelTests/KernelTestBase.php \Drupal\KernelTests\KernelTestBase
- 8 core/modules/simpletest/src/KernelTestBase.php \Drupal\simpletest\KernelTestBase
Same name and namespace in other branches
- 8.0 core/tests/Drupal/KernelTests/KernelTestBase.php \Drupal\KernelTests\KernelTestBase
Base class for functional integration tests.
Tests extending this base class can access files and the database, but the entire environment is initially empty. Drupal runs in a minimal mocked environment, comparable to the one in the early installer.
Unlike \Drupal\Tests\UnitTestCase, modules specified in the $modules property are automatically added to the service container for each test. The module/hook system is functional and operates on a fixed module list. Additional modules needed in a test may be loaded and added to the fixed module list.
Unlike \Drupal\simpletest\WebTestBase, the modules are only loaded, but not installed. Modules have to be installed manually, if needed.
@todo Extend ::setRequirementsFromAnnotation() and ::checkRequirements() to account for '@requires module'.
Hierarchy
- class \Drupal\KernelTests\KernelTestBase extends \Drupal\KernelTests\PHPUnit_Framework_TestCase implements ServiceProviderInterface uses AssertLegacyTrait, AssertContentTrait, AssertHelperTrait, RandomGeneratorTrait
Expanded class hierarchy of KernelTestBase
See also
\Drupal\Tests\KernelTestBase::$modules
\Drupal\Tests\KernelTestBase::enableModules()
25 files declare their use of KernelTestBase
- AliasStorageTest.php in core/
tests/ Drupal/ KernelTests/ Core/ Path/ AliasStorageTest.php - Contains \Drupal\KernelTests\Core\Path\AliasStorageTest.
- CacheCollectorTest.php in core/
tests/ Drupal/ KernelTests/ Core/ Cache/ CacheCollectorTest.php - \Drupal\KernelTests\Core\Cache\CacheCollectorTest.
- DbCommandBaseTest.php in core/
modules/ system/ tests/ src/ Kernel/ Scripts/ DbCommandBaseTest.php - Contains \Drupal\Tests\system\Kernel\Scripts\DbCommandBaseTest.
- DbDumpCommandTest.php in core/
modules/ system/ tests/ src/ Kernel/ Scripts/ DbDumpCommandTest.php - Contains \Drupal\Tests\system\Kernel\Scripts\DbDumpCommandTest.
- DbImportCommandTest.php in core/
modules/ system/ tests/ src/ Kernel/ Scripts/ DbImportCommandTest.php - Contains \Drupal\Tests\system\Kernel\Scripts\DbImportCommandTest.
File
- core/
tests/ Drupal/ KernelTests/ KernelTestBase.php, line 55 - Contains \Drupal\KernelTests\KernelTestBase.
Namespace
Drupal\KernelTestsView source
abstract class KernelTestBase extends \PHPUnit_Framework_TestCase implements ServiceProviderInterface {
use AssertLegacyTrait;
use AssertContentTrait;
use AssertHelperTrait;
use RandomGeneratorTrait;
/**
* {@inheritdoc}
*
* Back up and restore any global variables that may be changed by tests.
*
* @see self::runTestInSeparateProcess
*/
protected $backupGlobals = TRUE;
/**
* {@inheritdoc}
*
* Kernel tests are run in separate processes to prevent collisions between
* code that may be loaded by tests.
*/
protected $runTestInSeparateProcess = TRUE;
/**
* {@inheritdoc}
*
* Back up and restore static class properties that may be changed by tests.
*
* @see self::runTestInSeparateProcess
*/
protected $backupStaticAttributes = TRUE;
/**
* {@inheritdoc}
*
* Contains a few static class properties for performance.
*/
protected $backupStaticAttributesBlacklist = [
// Ignore static discovery/parser caches to speed up tests.
'Drupal\\Component\\Discovery\\YamlDiscovery' => [
'parsedFiles',
],
'Drupal\\Core\\DependencyInjection\\YamlFileLoader' => [
'yaml',
],
'Drupal\\Core\\Extension\\ExtensionDiscovery' => [
'files',
],
'Drupal\\Core\\Extension\\InfoParser' => [
'parsedInfos',
],
// Drupal::$container cannot be serialized.
'Drupal' => [
'container',
],
// Settings cannot be serialized.
'Drupal\\Core\\Site\\Settings' => [
'instance',
],
];
/**
* {@inheritdoc}
*
* Do not forward any global state from the parent process to the processes
* that run the actual tests.
*
* @see self::runTestInSeparateProcess
*/
protected $preserveGlobalState = FALSE;
/**
* @var \Composer\Autoload\Classloader
*/
protected $classLoader;
/**
* @var string
*/
protected $siteDirectory;
/**
* @var string
*/
protected $databasePrefix;
/**
* @var \Drupal\Core\DependencyInjection\ContainerBuilder
*/
protected $container;
/**
* @var \Drupal\Core\DependencyInjection\ContainerBuilder
*/
private static $initialContainerBuilder;
/**
* Modules to enable.
*
* Test classes extending this class, and any classes in the hierarchy up to
* this class, may specify individual lists of modules to enable by setting
* this property. The values of all properties in all classes in the class
* hierarchy are merged.
*
* @see \Drupal\Tests\KernelTestBase::enableModules()
* @see \Drupal\Tests\KernelTestBase::bootKernel()
*
* @var array
*/
public static $modules = array();
/**
* The virtual filesystem root directory.
*
* @var \org\bovigo\vfs\vfsStreamDirectory
*/
protected $vfsRoot;
/**
* @var int
*/
protected $expectedLogSeverity;
/**
* @var string
*/
protected $expectedLogMessage;
/**
* @todo Move into Config test base class.
* @var \Drupal\Core\Config\ConfigImporter
*/
protected $configImporter;
/**
* The app root.
*
* @var string
*/
protected $root;
/**
* Set to TRUE to strict check all configuration saved.
*
* @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
*
* @var bool
*/
protected $strictConfigSchema = TRUE;
/**
* {@inheritdoc}
*/
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
// Change the current dir to DRUPAL_ROOT.
chdir(static::getDrupalRoot());
}
/**
* Returns the drupal root directory.
*
* @return string
*/
protected static function getDrupalRoot() {
return dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->root = static::getDrupalRoot();
$this
->initFileCache();
$this
->bootEnvironment();
$this
->bootKernel();
}
/**
* Bootstraps a basic test environment.
*
* Should not be called by tests. Only visible for DrupalKernel integration
* tests.
*
* @see \Drupal\system\Tests\DrupalKernel\DrupalKernelTest
* @internal
*/
protected function bootEnvironment() {
$this->streamWrappers = array();
\Drupal::unsetContainer();
// @see /core/tests/bootstrap.php
$this->classLoader = $GLOBALS['loader'];
require_once $this->root . '/core/includes/bootstrap.inc';
// Set up virtual filesystem.
// Ensure that the generated test site directory does not exist already,
// which may happen with a large amount of concurrent threads and
// long-running tests.
do {
$suffix = mt_rand(100000, 999999);
$this->siteDirectory = 'sites/simpletest/' . $suffix;
$this->databasePrefix = 'simpletest' . $suffix;
} while (is_dir($this->root . '/' . $this->siteDirectory));
$this->vfsRoot = vfsStream::setup('root', NULL, array(
'sites' => array(
'simpletest' => array(
$suffix => array(),
),
),
));
$this->siteDirectory = vfsStream::url('root/sites/simpletest/' . $suffix);
mkdir($this->siteDirectory . '/files', 0775);
mkdir($this->siteDirectory . '/files/config/' . CONFIG_SYNC_DIRECTORY, 0775, TRUE);
// Ensure that all code that relies on drupal_valid_test_ua() can still be
// safely executed. This primarily affects the (test) site directory
// resolution (used by e.g. LocalStream and PhpStorage).
$this->databasePrefix = 'simpletest' . $suffix;
drupal_valid_test_ua($this->databasePrefix);
$settings = array(
'hash_salt' => get_class($this),
'file_public_path' => $this->siteDirectory . '/files',
// Disable Twig template caching/dumping.
'twig_cache' => FALSE,
);
new Settings($settings);
$GLOBALS['config_directories'] = array(
CONFIG_SYNC_DIRECTORY => $this->siteDirectory . '/files/config/sync',
);
foreach (Database::getAllConnectionInfo() as $key => $targets) {
Database::removeConnection($key);
}
Database::addConnectionInfo('default', 'default', $this
->getDatabaseConnectionInfo()['default']);
}
/**
* Bootstraps a kernel for a test.
*/
private function bootKernel() {
$this
->setSetting('container_yamls', []);
// Allow for test-specific overrides.
$settings_services_file = $this->root . '/sites/default' . '/testing.services.yml';
if (file_exists($settings_services_file)) {
// Copy the testing-specific service overrides in place.
$testing_services_file = $this->root . '/' . $this->siteDirectory . '/services.yml';
copy($settings_services_file, $testing_services_file);
$this
->setSetting('container_yamls', [
$testing_services_file,
]);
}
// Allow for global test environment overrides.
if (file_exists($test_env = $this->root . '/sites/default/testing.services.yml')) {
$GLOBALS['conf']['container_yamls']['testing'] = $test_env;
}
// Add this test class as a service provider.
$GLOBALS['conf']['container_service_providers']['test'] = $this;
$modules = self::getModulesToEnable(get_class($this));
// Prepare a precompiled container for all tests of this class.
// Substantially improves performance, since ContainerBuilder::compile()
// is very expensive. Encourages testing best practices (small tests).
// Normally a setUpBeforeClass() operation, but object scope is required to
// inject $this test class instance as a service provider (see above).
$rc = new \ReflectionClass(get_class($this));
$test_method_count = count(array_filter($rc
->getMethods(), function ($method) {
// PHPUnit's @test annotations are intentionally ignored/not supported.
return strpos($method
->getName(), 'test') === 0;
}));
if ($test_method_count > 1 && !$this
->isTestInIsolation()) {
// Clone a precompiled, empty ContainerBuilder instance for each test.
$container = $this
->getCompiledContainerBuilder($modules);
}
// Bootstrap the kernel. Do not use createFromRequest() to retain Settings.
$kernel = new DrupalKernel('testing', $this->classLoader, FALSE);
$kernel
->setSitePath($this->siteDirectory);
// Boot the precompiled container. The kernel will enhance it with synthetic
// services.
if (isset($container)) {
$kernel
->setContainer($container);
unset($container);
}
elseif ($modules && ($extensions = $this
->getExtensionsForModules($modules))) {
$kernel
->updateModules($extensions, $extensions);
}
// DrupalKernel::boot() is not sufficient as it does not invoke preHandle(),
// which is required to initialize legacy global variables.
$request = Request::create('/');
$kernel
->prepareLegacyRequest($request);
// register() is only called if a new container was built/compiled.
$this->container = $kernel
->getContainer();
// Ensure database tasks have been run.
require_once __DIR__ . '/../../../includes/install.inc';
$connection = Database::getConnection();
$errors = db_installer_object($connection
->driver())
->runTasks();
if (!empty($errors)) {
$this
->fail('Failed to run installer database tasks: ' . implode(', ', $errors));
}
if ($modules) {
$this->container
->get('module_handler')
->loadAll();
}
// Write the core.extension configuration.
// Required for ConfigInstaller::installDefaultConfig() to work.
$this->container
->get('config.storage')
->write('core.extension', array(
'module' => array_fill_keys($modules, 0),
'theme' => array(),
));
$settings = Settings::getAll();
$settings['php_storage']['default'] = [
'class' => '\\Drupal\\Component\\PhpStorage\\FileStorage',
];
new Settings($settings);
}
/**
* Configuration accessor for tests. Returns non-overridden configuration.
*
* @param string $name
* The configuration name.
*
* @return \Drupal\Core\Config\Config
* The configuration object with original configuration data.
*/
protected function config($name) {
return $this->container
->get('config.factory')
->getEditable($name);
}
/**
* Returns the Database connection info to be used for this test.
*
* This method only exists for tests of the Database component itself, because
* they require multiple database connections. Each SQLite :memory: connection
* creates a new/separate database in memory. A shared-memory SQLite file URI
* triggers PHP open_basedir/allow_url_fopen/allow_url_include restrictions.
* Due to that, Database tests are running against a SQLite database that is
* located in an actual file in the system's temporary directory.
*
* Other tests should not override this method.
*
* @return array
* A Database connection info array.
*
* @internal
*/
protected function getDatabaseConnectionInfo() {
// If the test is run with argument dburl then use it.
$db_url = getenv('SIMPLETEST_DB');
if (empty($db_url)) {
$this
->markTestSkipped('There is no database connection so no tests can be run. You must provide a SIMPLETEST_DB environment variable to run PHPUnit based functional tests outside of run-tests.sh. See https://www.drupal.org/node/2116263#skipped-tests for more information.');
}
else {
$database = Database::convertDbUrlToConnectionInfo($db_url, $this->root);
Database::addConnectionInfo('default', 'default', $database);
}
// Clone the current connection and replace the current prefix.
$connection_info = Database::getConnectionInfo('default');
if (!empty($connection_info)) {
Database::renameConnection('default', 'simpletest_original_default');
foreach ($connection_info as $target => $value) {
// Replace the full table prefix definition to ensure that no table
// prefixes of the test runner leak into the test.
$connection_info[$target]['prefix'] = array(
'default' => $value['prefix']['default'] . $this->databasePrefix,
);
}
}
return $connection_info;
}
/**
* Prepares a precompiled ContainerBuilder for all tests of this class.
*
* Avoids repetitive calls to ContainerBuilder::compile(), which is very slow.
*
* Based on the (always identical) list of $modules to enable, an initial
* container is compiled, all instantiated services are reset/removed, and
* this precompiled container is stored in a static class property. (Static,
* because PHPUnit instantiates a new class instance for each test *method*.)
*
* This method is not invoked if there is only a single test method. It is
* also not invoked for tests running in process isolation (since each test
* method runs in a separate process).
*
* The ContainerBuilder is not dumped into the filesystem (which would yield
* an actually compiled Container class), because
*
* 1. PHP code cannot be unloaded, so e.g. 900 tests would load 900 different,
* full Container classes into memory, quickly exceeding any sensible
* memory consumption (GigaBytes).
* 2. Dumping a Container class requires to actually write to the system's
* temporary directory. This is not really easy with vfs, because vfs
* doesn't support yet "include 'vfs://container.php'.". Maybe we could fix
* that upstream.
* 3. PhpDumper is very slow on its own.
*
* @param string[] $modules
* The list of modules to enable.
*
* @return \Drupal\Core\DependencyInjection\ContainerBuilder
* A clone of the precompiled, empty service container.
*/
private function getCompiledContainerBuilder(array $modules) {
if (!isset(self::$initialContainerBuilder)) {
$kernel = new DrupalKernel('testing', $this->classLoader, FALSE);
$kernel
->setSitePath($this->siteDirectory);
if ($modules && ($extensions = $this
->getExtensionsForModules($modules))) {
$kernel
->updateModules($extensions, $extensions);
}
$kernel
->boot();
self::$initialContainerBuilder = $kernel
->getContainer();
// Remove all instantiated services, so the container is safe for cloning.
// Technically, ContainerBuilder::set($id, NULL) removes each definition,
// but the container is compiled/frozen already.
foreach (self::$initialContainerBuilder
->getServiceIds() as $id) {
self::$initialContainerBuilder
->set($id, NULL);
}
// Destruct and trigger garbage collection.
\Drupal::unsetContainer();
$kernel
->shutdown();
$kernel = NULL;
// @see register()
$this->container = NULL;
}
$container = clone self::$initialContainerBuilder;
return $container;
}
/**
* Initializes the FileCache component.
*
* We can not use the Settings object in a component, that's why we have to do
* it here instead of \Drupal\Component\FileCache\FileCacheFactory.
*/
protected function initFileCache() {
$configuration = Settings::get('file_cache');
// Provide a default configuration, if not set.
if (!isset($configuration['default'])) {
$configuration['default'] = [
'class' => FileCache::class,
'cache_backend_class' => NULL,
'cache_backend_configuration' => [],
];
// @todo Use extension_loaded('apcu') for non-testbot
// https://www.drupal.org/node/2447753.
if (function_exists('apc_fetch')) {
$configuration['default']['cache_backend_class'] = ApcuFileCacheBackend::class;
}
}
FileCacheFactory::setConfiguration($configuration);
FileCacheFactory::setPrefix(Settings::getApcuPrefix('file_cache', $this->root));
}
/**
* Returns Extension objects for $modules to enable.
*
* @param string[] $modules
* The list of modules to enable.
*
* @return \Drupal\Core\Extension\Extension[]
* Extension objects for $modules, keyed by module name.
*
* @throws \PHPUnit_Framework_Exception
* If a module is not available.
*
* @see \Drupal\Tests\KernelTestBase::enableModules()
* @see \Drupal\Core\Extension\ModuleHandler::add()
*/
private function getExtensionsForModules(array $modules) {
$extensions = array();
$discovery = new ExtensionDiscovery($this->root);
$discovery
->setProfileDirectories(array());
$list = $discovery
->scan('module');
foreach ($modules as $name) {
if (!isset($list[$name])) {
throw new \PHPUnit_Framework_Exception("Unavailable module: '{$name}'. If this module needs to be downloaded separately, annotate the test class with '@requires module {$name}'.");
}
$extensions[$name] = $list[$name];
}
return $extensions;
}
/**
* Registers test-specific services.
*
* Extend this method in your test to register additional services. This
* method is called whenever the kernel is rebuilt.
*
* @param \Drupal\Core\DependencyInjection\ContainerBuilder $container
* The service container to enhance.
*
* @see \Drupal\Tests\KernelTestBase::bootKernel()
*/
public function register(ContainerBuilder $container) {
// Keep the container object around for tests.
$this->container = $container;
$container
->register('flood', 'Drupal\\Core\\Flood\\MemoryBackend')
->addArgument(new Reference('request_stack'));
$container
->register('lock', 'Drupal\\Core\\Lock\\NullLockBackend');
$container
->register('cache_factory', 'Drupal\\Core\\Cache\\MemoryBackendFactory');
$container
->register('keyvalue.memory', 'Drupal\\Core\\KeyValueStore\\KeyValueMemoryFactory')
->addTag('persist');
$container
->setAlias('keyvalue', 'keyvalue.memory');
if ($this->strictConfigSchema) {
$container
->register('simpletest.config_schema_checker', 'Drupal\\Core\\Config\\Testing\\ConfigSchemaChecker')
->addArgument(new Reference('config.typed'))
->addTag('event_subscriber');
}
if ($container
->hasDefinition('path_processor_alias')) {
// Prevent the alias-based path processor, which requires a url_alias db
// table, from being registered to the path processor manager. We do this
// by removing the tags that the compiler pass looks for. This means the
// url generator can safely be used within tests.
$container
->getDefinition('path_processor_alias')
->clearTag('path_processor_inbound')
->clearTag('path_processor_outbound');
}
if ($container
->hasDefinition('password')) {
$container
->getDefinition('password')
->setArguments(array(
1,
));
}
}
/**
* {@inheritdoc}
*/
protected function assertPostConditions() {
// Execute registered Drupal shutdown functions prior to tearing down.
// @see _drupal_shutdown_function()
$callbacks =& drupal_register_shutdown_function();
while ($callback = array_shift($callbacks)) {
call_user_func_array($callback['callback'], $callback['arguments']);
}
// Shut down the kernel (if bootKernel() was called).
// @see \Drupal\system\Tests\DrupalKernel\DrupalKernelTest
if ($this->container) {
$this->container
->get('kernel')
->shutdown();
}
// Fail in case any (new) shutdown functions exist.
$this
->assertCount(0, drupal_register_shutdown_function(), 'Unexpected Drupal shutdown callbacks exist after running shutdown functions.');
parent::assertPostConditions();
}
/**
* {@inheritdoc}
*/
protected function tearDown() {
// Destroy the testing kernel.
if (isset($this->kernel)) {
$this->kernel
->shutdown();
}
// Remove all prefixed tables.
$original_connection_info = Database::getConnectionInfo('simpletest_original_default');
$original_prefix = $original_connection_info['default']['prefix']['default'];
$test_connection_info = Database::getConnectionInfo('default');
$test_prefix = $test_connection_info['default']['prefix']['default'];
if ($original_prefix != $test_prefix) {
$tables = Database::getConnection()
->schema()
->findTables('%');
foreach ($tables as $table) {
if (Database::getConnection()
->schema()
->dropTable($table)) {
unset($tables[$table]);
}
}
}
// Free up memory: Own properties.
$this->classLoader = NULL;
$this->vfsRoot = NULL;
$this->configImporter = NULL;
// Free up memory: Custom test class properties.
// Note: Private properties cannot be cleaned up.
$rc = new \ReflectionClass(__CLASS__);
$blacklist = array();
foreach ($rc
->getProperties() as $property) {
$blacklist[$property->name] = $property
->getDeclaringClass()->name;
}
$rc = new \ReflectionClass($this);
foreach ($rc
->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $property) {
if (!$property
->isStatic() && !isset($blacklist[$property->name])) {
$this->{$property->name} = NULL;
}
}
// Clean FileCache cache.
FileCache::reset();
// Clean up statics, container, and settings.
if (function_exists('drupal_static_reset')) {
drupal_static_reset();
}
\Drupal::unsetContainer();
$this->container = NULL;
new Settings(array());
parent::tearDown();
}
/**
* @after
*
* Additional tear down method to close the connection at the end.
*/
public function tearDownCloseDatabaseConnection() {
// Destroy the database connection, which for example removes the memory
// from sqlite in memory.
foreach (Database::getAllConnectionInfo() as $key => $targets) {
Database::removeConnection($key);
}
}
/**
* {@inheritdoc}
*/
public static function tearDownAfterClass() {
// Free up memory: Precompiled container.
self::$initialContainerBuilder = NULL;
parent::tearDownAfterClass();
}
/**
* Installs default configuration for a given list of modules.
*
* @param string|string[] $modules
* A list of modules for which to install default configuration.
*
* @throws \LogicException
* If any module in $modules is not enabled.
*/
protected function installConfig($modules) {
foreach ((array) $modules as $module) {
if (!$this->container
->get('module_handler')
->moduleExists($module)) {
throw new \LogicException("{$module} module is not enabled.");
}
$this->container
->get('config.installer')
->installDefaultConfig('module', $module);
}
}
/**
* Installs database tables from a module schema definition.
*
* @param string $module
* The name of the module that defines the table's schema.
* @param string|array $tables
* The name or an array of the names of the tables to install.
*
* @throws \LogicException
* If $module is not enabled or the table schema cannot be found.
*/
protected function installSchema($module, $tables) {
// drupal_get_module_schema() is technically able to install a schema
// of a non-enabled module, but its ability to load the module's .install
// file depends on many other factors. To prevent differences in test
// behavior and non-reproducible test failures, we only allow the schema of
// explicitly loaded/enabled modules to be installed.
if (!$this->container
->get('module_handler')
->moduleExists($module)) {
throw new \LogicException("{$module} module is not enabled.");
}
$tables = (array) $tables;
foreach ($tables as $table) {
$schema = drupal_get_module_schema($module, $table);
if (empty($schema)) {
throw new \LogicException("{$module} module does not define a schema for table '{$table}'.");
}
$this->container
->get('database')
->schema()
->createTable($table, $schema);
}
}
/**
* Installs the storage schema for a specific entity type.
*
* @param string $entity_type_id
* The ID of the entity type.
*/
protected function installEntitySchema($entity_type_id) {
/** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */
$entity_manager = $this->container
->get('entity.manager');
$entity_type = $entity_manager
->getDefinition($entity_type_id);
$entity_manager
->onEntityTypeCreate($entity_type);
// For test runs, the most common storage backend is a SQL database. For
// this case, ensure the tables got created.
$storage = $entity_manager
->getStorage($entity_type_id);
if ($storage instanceof SqlEntityStorageInterface) {
$tables = $storage
->getTableMapping()
->getTableNames();
$db_schema = $this->container
->get('database')
->schema();
$all_tables_exist = TRUE;
foreach ($tables as $table) {
if (!$db_schema
->tableExists($table)) {
$this
->fail(SafeMarkup::format('Installed entity type table for the %entity_type entity type: %table', array(
'%entity_type' => $entity_type_id,
'%table' => $table,
)));
$all_tables_exist = FALSE;
}
}
if ($all_tables_exist) {
$this
->pass(SafeMarkup::format('Installed entity type tables for the %entity_type entity type: %tables', array(
'%entity_type' => $entity_type_id,
'%tables' => '{' . implode('}, {', $tables) . '}',
)));
}
}
}
/**
* Enables modules for this test.
*
* @param string[] $modules
* A list of modules to enable. Dependencies are not resolved; i.e.,
* multiple modules have to be specified individually. The modules are only
* added to the active module list and loaded; i.e., their database schema
* is not installed. hook_install() is not invoked. A custom module weight
* is not applied.
*
* @throws \LogicException
* If any module in $modules is already enabled.
* @throws \RuntimeException
* If a module is not enabled after enabling it.
*/
protected function enableModules(array $modules) {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
if ($trace[1]['function'] === 'setUp') {
trigger_error('KernelTestBase::enableModules() should not be called from setUp(). Use the $modules property instead.', E_USER_DEPRECATED);
}
unset($trace);
// Perform an ExtensionDiscovery scan as this function may receive a
// profile that is not the current profile, and we don't yet have a cached
// way to receive inactive profile information.
// @todo Remove as part of https://www.drupal.org/node/2186491
$listing = new ExtensionDiscovery(\Drupal::root());
$module_list = $listing
->scan('module');
// In ModuleHandlerTest we pass in a profile as if it were a module.
$module_list += $listing
->scan('profile');
// Set the list of modules in the extension handler.
$module_handler = $this->container
->get('module_handler');
// Write directly to active storage to avoid early instantiation of
// the event dispatcher which can prevent modules from registering events.
$active_storage = $this->container
->get('config.storage');
$extension_config = $active_storage
->read('core.extension');
foreach ($modules as $module) {
if ($module_handler
->moduleExists($module)) {
throw new \LogicException("{$module} module is already enabled.");
}
$module_handler
->addModule($module, $module_list[$module]
->getPath());
// Maintain the list of enabled modules in configuration.
$extension_config['module'][$module] = 0;
}
$active_storage
->write('core.extension', $extension_config);
// Update the kernel to make their services available.
$extensions = $module_handler
->getModuleList();
$this->container
->get('kernel')
->updateModules($extensions, $extensions);
// Ensure isLoaded() is TRUE in order to make
// \Drupal\Core\Theme\ThemeManagerInterface::render() work.
// Note that the kernel has rebuilt the container; this $module_handler is
// no longer the $module_handler instance from above.
$module_handler = $this->container
->get('module_handler');
$module_handler
->reload();
foreach ($modules as $module) {
if (!$module_handler
->moduleExists($module)) {
throw new \RuntimeException("{$module} module is not enabled after enabling it.");
}
}
}
/**
* Disables modules for this test.
*
* @param string[] $modules
* A list of modules to disable. Dependencies are not resolved; i.e.,
* multiple modules have to be specified with dependent modules first.
* Code of previously enabled modules is still loaded. The modules are only
* removed from the active module list.
*
* @throws \LogicException
* If any module in $modules is already disabled.
* @throws \RuntimeException
* If a module is not disabled after disabling it.
*/
protected function disableModules(array $modules) {
// Unset the list of modules in the extension handler.
$module_handler = $this->container
->get('module_handler');
$module_filenames = $module_handler
->getModuleList();
$extension_config = $this
->config('core.extension');
foreach ($modules as $module) {
if (!$module_handler
->moduleExists($module)) {
throw new \LogicException("{$module} module cannot be disabled because it is not enabled.");
}
unset($module_filenames[$module]);
$extension_config
->clear('module.' . $module);
}
$extension_config
->save();
$module_handler
->setModuleList($module_filenames);
$module_handler
->resetImplementations();
// Update the kernel to remove their services.
$this->container
->get('kernel')
->updateModules($module_filenames, $module_filenames);
// Ensure isLoaded() is TRUE in order to make _theme() work.
// Note that the kernel has rebuilt the container; this $module_handler is
// no longer the $module_handler instance from above.
$module_handler = $this->container
->get('module_handler');
$module_handler
->reload();
foreach ($modules as $module) {
if ($module_handler
->moduleExists($module)) {
throw new \RuntimeException("{$module} module is not disabled after disabling it.");
}
}
}
/**
* Renders a render array.
*
* @param array $elements
* The elements to render.
*
* @return string
* The rendered string output (typically HTML).
*/
protected function render(array &$elements) {
// Use the bare HTML page renderer to render our links.
$renderer = $this->container
->get('bare_html_page_renderer');
$response = $renderer
->renderBarePage($elements, '', $this->container
->get('theme.manager')
->getActiveTheme()
->getName());
// Glean the content from the response object.
$content = $response
->getContent();
$this
->setRawContent($content);
$this
->verbose('<pre style="white-space: pre-wrap">' . Html::escape($content));
return $content;
}
/**
* Sets an in-memory Settings variable.
*
* @param string $name
* The name of the setting to set.
* @param bool|string|int|array|null $value
* The value to set. Note that array values are replaced entirely; use
* \Drupal\Core\Site\Settings::get() to perform custom merges.
*/
protected function setSetting($name, $value) {
$settings = Settings::getAll();
$settings[$name] = $value;
new Settings($settings);
}
/**
* Returns a ConfigImporter object to import test configuration.
*
* @return \Drupal\Core\Config\ConfigImporter
*
* @todo Move into Config-specific test base class.
*/
protected function configImporter() {
if (!$this->configImporter) {
// Set up the ConfigImporter object for testing.
$storage_comparer = new StorageComparer($this->container
->get('config.storage.sync'), $this->container
->get('config.storage'), $this->container
->get('config.manager'));
$this->configImporter = new ConfigImporter($storage_comparer, $this->container
->get('event_dispatcher'), $this->container
->get('config.manager'), $this->container
->get('lock'), $this->container
->get('config.typed'), $this->container
->get('module_handler'), $this->container
->get('module_installer'), $this->container
->get('theme_handler'), $this->container
->get('string_translation'));
}
// Always recalculate the changelist when called.
return $this->configImporter
->reset();
}
/**
* Copies configuration objects from a source storage to a target storage.
*
* @param \Drupal\Core\Config\StorageInterface $source_storage
* The source config storage.
* @param \Drupal\Core\Config\StorageInterface $target_storage
* The target config storage.
*
* @todo Move into Config-specific test base class.
*/
protected function copyConfig(StorageInterface $source_storage, StorageInterface $target_storage) {
$target_storage
->deleteAll();
foreach ($source_storage
->listAll() as $name) {
$target_storage
->write($name, $source_storage
->read($name));
}
}
/**
* Stops test execution.
*/
protected function stop() {
$this
->getTestResultObject()
->stop();
}
/**
* Dumps the current state of the virtual filesystem to STDOUT.
*/
protected function vfsDump() {
vfsStream::inspect(new vfsStreamPrintVisitor());
}
/**
* Returns the modules to enable for this test.
*
* @param string $class
* The fully-qualified class name of this test.
*
* @return array
*/
private static function getModulesToEnable($class) {
$modules = array();
while ($class) {
if (property_exists($class, 'modules')) {
// Only add the modules, if the $modules property was not inherited.
$rp = new \ReflectionProperty($class, 'modules');
if ($rp->class == $class) {
$modules[$class] = $class::$modules;
}
}
$class = get_parent_class($class);
}
// Modules have been collected in reverse class hierarchy order; modules
// defined by base classes should be sorted first. Then, merge the results
// together.
$modules = array_reverse($modules);
return call_user_func_array('array_merge_recursive', $modules);
}
/**
* {@inheritdoc}
*/
protected function prepareTemplate(\Text_Template $template) {
$bootstrap_globals = '';
// Fix missing bootstrap.php when $preserveGlobalState is FALSE.
// @see https://github.com/sebastianbergmann/phpunit/pull/797
$bootstrap_globals .= '$__PHPUNIT_BOOTSTRAP = ' . var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], TRUE) . ";\n";
// Avoid repetitive test namespace discoveries to improve performance.
// @see /core/tests/bootstrap.php
$bootstrap_globals .= '$namespaces = ' . var_export($GLOBALS['namespaces'], TRUE) . ";\n";
$template
->setVar(array(
'constants' => '',
'included_files' => '',
'globals' => $bootstrap_globals,
));
}
/**
* Returns whether the current test runs in isolation.
*
* @return bool
*
* @see https://github.com/sebastianbergmann/phpunit/pull/1350
*/
protected function isTestInIsolation() {
return function_exists('__phpunit_run_isolated_test');
}
/**
* BC: Automatically resolve former KernelTestBase class properties.
*
* Test authors should follow the provided instructions and adjust their tests
* accordingly.
*
* @deprecated in Drupal 8.0.x, will be removed before Drupal 8.2.0.
*/
public function __get($name) {
if (in_array($name, array(
'public_files_directory',
'private_files_directory',
'temp_files_directory',
'translation_files_directory',
))) {
// @comment it in again.
trigger_error(sprintf("KernelTestBase::\$%s no longer exists. Use the regular API method to retrieve it instead (e.g., Settings).", $name), E_USER_DEPRECATED);
switch ($name) {
case 'public_files_directory':
return Settings::get('file_public_path', \Drupal::service('site.path') . '/files');
case 'private_files_directory':
return $this->container
->get('config.factory')
->get('system.file')
->get('path.private');
case 'temp_files_directory':
return file_directory_temp();
case 'translation_files_directory':
return Settings::get('file_public_path', \Drupal::service('site.path') . '/translations');
}
}
if ($name === 'configDirectories') {
trigger_error(sprintf("KernelTestBase::\$%s no longer exists. Use config_get_config_directory() directly instead.", $name), E_USER_DEPRECATED);
return array(
CONFIG_SYNC_DIRECTORY => config_get_config_directory(CONFIG_SYNC_DIRECTORY),
);
}
$denied = array(
// @see \Drupal\simpletest\TestBase
'testId',
'timeLimit',
'results',
'assertions',
'skipClasses',
'verbose',
'verboseId',
'verboseClassName',
'verboseDirectory',
'verboseDirectoryUrl',
'dieOnFail',
'kernel',
// @see \Drupal\simpletest\TestBase::prepareEnvironment()
'generatedTestFiles',
// @see \Drupal\simpletest\KernelTestBase::containerBuild()
'keyValueFactory',
);
if (in_array($name, $denied) || strpos($name, 'original') === 0) {
throw new \RuntimeException(sprintf('TestBase::$%s property no longer exists', $name));
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
AssertContentTrait:: |
protected | property | The current raw content. | |
AssertContentTrait:: |
protected | property | The drupalSettings value from the current raw $content. | |
AssertContentTrait:: |
protected | property | The XML structure parsed from the current raw $content. | 2 |
AssertContentTrait:: |
protected | property | The plain-text content of raw $content (text nodes). | |
AssertContentTrait:: |
protected | function | Passes if the raw text IS found escaped on the loaded page, fail otherwise. | |
AssertContentTrait:: |
protected | function | Asserts that a field exists with the given name or ID. | |
AssertContentTrait:: |
protected | function | Asserts that a field exists with the given ID and value. | |
AssertContentTrait:: |
protected | function | Asserts that a field exists with the given name and value. | |
AssertContentTrait:: |
protected | function | Asserts that a field exists in the current page by the given XPath. | |
AssertContentTrait:: |
protected | function | Asserts that a checkbox field in the current page is checked. | |
AssertContentTrait:: |
protected | function | Asserts that a field exists in the current page with a given Xpath result. | |
AssertContentTrait:: |
protected | function | Passes if a link with the specified label is found. | |
AssertContentTrait:: |
protected | function | Passes if a link containing a given href (part) is found. | |
AssertContentTrait:: |
protected | function | Asserts that each HTML ID is used for just a single element. | |
AssertContentTrait:: |
protected | function | Passes if the raw text IS NOT found escaped on the loaded page, fail otherwise. | |
AssertContentTrait:: |
protected | function | Asserts that a field does not exist with the given name or ID. | |
AssertContentTrait:: |
protected | function | Asserts that a field does not exist with the given ID and value. | |
AssertContentTrait:: |
protected | function | Asserts that a field does not exist with the given name and value. | |
AssertContentTrait:: |
protected | function | Asserts that a field does not exist or its value does not match, by XPath. | |
AssertContentTrait:: |
protected | function | Asserts that a checkbox field in the current page is not checked. | |
AssertContentTrait:: |
protected | function | Passes if a link with the specified label is not found. | |
AssertContentTrait:: |
protected | function | Passes if a link containing a given href (part) is not found. | |
AssertContentTrait:: |
protected | function | Passes if a link containing a given href is not found in the main region. | |
AssertContentTrait:: |
protected | function | Asserts that a select option in the current page does not exist. | |
AssertContentTrait:: |
protected | function | Asserts that a select option in the current page is not checked. | |
AssertContentTrait:: |
protected | function | Triggers a pass if the perl regex pattern is not found in raw content. | |
AssertContentTrait:: |
protected | function | Passes if the raw text is NOT found on the loaded page, fail otherwise. | |
AssertContentTrait:: |
protected | function | Passes if the page (with HTML stripped) does not contains the text. | |
AssertContentTrait:: |
protected | function | Pass if the page title is not the given string. | |
AssertContentTrait:: |
protected | function | Passes if the text is found MORE THAN ONCE on the text version of the page. | |
AssertContentTrait:: |
protected | function | Asserts that a select option in the current page exists. | |
AssertContentTrait:: |
protected | function | Asserts that a select option in the current page is checked. | |
AssertContentTrait:: |
protected | function | Asserts that a select option in the current page is checked. | |
AssertContentTrait:: |
protected | function | Asserts that a select option in the current page exists. | |
AssertContentTrait:: |
protected | function | Triggers a pass if the Perl regex pattern is found in the raw content. | |
AssertContentTrait:: |
protected | function | Passes if the raw text IS found on the loaded page, fail otherwise. | |
AssertContentTrait:: |
protected | function | Passes if the page (with HTML stripped) contains the text. | |
AssertContentTrait:: |
protected | function | Helper for assertText and assertNoText. | |
AssertContentTrait:: |
protected | function | Asserts that a Perl regex pattern is found in the plain-text content. | |
AssertContentTrait:: |
protected | function | Asserts themed output. | |
AssertContentTrait:: |
protected | function | Pass if the page title is the given string. | |
AssertContentTrait:: |
protected | function | Passes if the text is found ONLY ONCE on the text version of the page. | |
AssertContentTrait:: |
protected | function | Helper for assertUniqueText and assertNoUniqueText. | |
AssertContentTrait:: |
protected | function | Builds an XPath query. | |
AssertContentTrait:: |
protected | function | Helper: Constructs an XPath for the given set of attributes and value. | |
AssertContentTrait:: |
protected | function | Searches elements using a CSS selector in the raw content. | |
AssertContentTrait:: |
protected | function | Get all option elements, including nested options, in a select. | |
AssertContentTrait:: |
protected | function | Gets the value of drupalSettings for the currently-loaded page. | |
AssertContentTrait:: |
protected | function | Gets the current raw content. | |
AssertContentTrait:: |
protected | function | Get the selected value from a select field. | |
AssertContentTrait:: |
protected | function | Retrieves the plain-text content from the current raw content. | |
AssertContentTrait:: |
protected | function | Get the current URL from the cURL handler. | 1 |
AssertContentTrait:: |
protected | function | Parse content returned from curlExec using DOM and SimpleXML. | |
AssertContentTrait:: |
protected | function | Removes all white-space between HTML tags from the raw content. | |
AssertContentTrait:: |
protected | function | Sets the value of drupalSettings for the currently-loaded page. | |
AssertContentTrait:: |
protected | function | Sets the raw content (e.g. HTML). | |
AssertContentTrait:: |
protected | function | Performs an xpath search on the contents of the internal browser. | |
AssertHelperTrait:: |
protected | function | Casts MarkupInterface objects into strings. | |
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
public static | function | ||
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
public static | function | ||
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | ||
KernelTestBase:: |
protected | property | Back up and restore any global variables that may be changed by tests. | |
KernelTestBase:: |
protected | property | Back up and restore static class properties that may be changed by tests. | |
KernelTestBase:: |
protected | property | Contains a few static class properties for performance. | |
KernelTestBase:: |
protected | property | ||
KernelTestBase:: |
protected | property | @todo Move into Config test base class. | |
KernelTestBase:: |
protected | property | ||
KernelTestBase:: |
protected | property | ||
KernelTestBase:: |
protected | property | ||
KernelTestBase:: |
protected | property | ||
KernelTestBase:: |
private static | property | ||
KernelTestBase:: |
public static | property | Modules to enable. | |
KernelTestBase:: |
protected | property | Do not forward any global state from the parent process to the processes that run the actual tests. | |
KernelTestBase:: |
protected | property | The app root. | |
KernelTestBase:: |
protected | property | Kernel tests are run in separate processes to prevent collisions between code that may be loaded by tests. | |
KernelTestBase:: |
protected | property | ||
KernelTestBase:: |
protected | property | Set to TRUE to strict check all configuration saved. | |
KernelTestBase:: |
protected | property | The virtual filesystem root directory. | |
KernelTestBase:: |
protected | function | ||
KernelTestBase:: |
protected | function | Bootstraps a basic test environment. | |
KernelTestBase:: |
private | function | Bootstraps a kernel for a test. | |
KernelTestBase:: |
protected | function | Configuration accessor for tests. Returns non-overridden configuration. | |
KernelTestBase:: |
protected | function | Returns a ConfigImporter object to import test configuration. | |
KernelTestBase:: |
protected | function | Copies configuration objects from a source storage to a target storage. | |
KernelTestBase:: |
protected | function | Disables modules for this test. | |
KernelTestBase:: |
protected | function | Enables modules for this test. | |
KernelTestBase:: |
private | function | Prepares a precompiled ContainerBuilder for all tests of this class. | |
KernelTestBase:: |
protected | function | Returns the Database connection info to be used for this test. | |
KernelTestBase:: |
protected static | function | Returns the drupal root directory. | |
KernelTestBase:: |
private | function | Returns Extension objects for $modules to enable. | |
KernelTestBase:: |
private static | function | Returns the modules to enable for this test. | |
KernelTestBase:: |
protected | function | Initializes the FileCache component. | |
KernelTestBase:: |
protected | function | Installs default configuration for a given list of modules. | |
KernelTestBase:: |
protected | function | Installs the storage schema for a specific entity type. | |
KernelTestBase:: |
protected | function | Installs database tables from a module schema definition. | |
KernelTestBase:: |
protected | function | Returns whether the current test runs in isolation. | |
KernelTestBase:: |
protected | function | ||
KernelTestBase:: |
public | function |
Registers test-specific services. Overrides ServiceProviderInterface:: |
2 |
KernelTestBase:: |
protected | function | Renders a render array. | |
KernelTestBase:: |
protected | function | Sets an in-memory Settings variable. | |
KernelTestBase:: |
protected | function | 170 | |
KernelTestBase:: |
public static | function | ||
KernelTestBase:: |
protected | function | Stops test execution. | |
KernelTestBase:: |
protected | function | 6 | |
KernelTestBase:: |
public static | function | ||
KernelTestBase:: |
public | function | @after | |
KernelTestBase:: |
protected | function | Dumps the current state of the virtual filesystem to STDOUT. | |
KernelTestBase:: |
public | function | BC: Automatically resolve former KernelTestBase class properties. | |
RandomGeneratorTrait:: |
protected | property | The random generator. | |
RandomGeneratorTrait:: |
protected | function | Gets the random generator for the utility methods. | |
RandomGeneratorTrait:: |
protected | function | Generates a unique random string containing letters and numbers. | |
RandomGeneratorTrait:: |
public | function | Generates a random PHP object. | |
RandomGeneratorTrait:: |
public | function | Generates a pseudo-random string of ASCII characters of codes 32 to 126. | |
RandomGeneratorTrait:: |
public | function | Callback for random string validation. |