Previously, Drupal core provided two base classes for authoring tests; one for unit tests and one for functional/browser/integration web tests:
-
Drupal\simpletest\
UnitTestBase
(DrupalUnitTestCase
in Drupal 7) sets up essentially nothing. It only prevents the test from changing the environment of the test runner that is executing the test. The test class is on its own, and to unit test a certain function it needs to mock and inject any possibly needed dependencies. -
Drupal\simpletest\
WebTestBase
installs an entire Drupal site using a specified installation profile through an internal browser. The test class can install further extensions, use the internal browser to access and assert expectations in the test site, and it can invoke all functionality that is available in the Drupal site, as if the site would have been installed regularly.
Note:Note: UnitTestBase has been removed in favor of PHPUnit unit tests in Drupal 8.
Even though many tests are only unit-testing functions, they had to use WebTestBase, just because functions being called by the test happen to contain hook invocations or ModuleHandlerInterface::alter()
. They have no use for the entire installed Drupal site and also do not need the internal browser. For such tests, Drupal 8 introduces:
-
Drupal\simpletest\
KernelTestBase
sets up a completely empty Drupal environment, but with a functional module/hook/extension system and configuration system. It also sets up a database and filesystem, but both are empty.KernelTestBase
replaces a range of core services with in-memory implementations (e.g., Cache, Lock, KeyValue) so they are functional but do not try to store data persistently (because there is no storage). As in a unit test, the test class is on its own, but it is able to enable modules (loading their code to participate in hooks) and to install modules (including their database schema). This allows to perform API-level integration tests for functionality that cannot be properly injected currently.
The primary reason for introducing KernelTestBase is test performance. A web test requires significant amount of time to install the internal Drupal site (which isn't even used by the test in the end).
When is it appropriate to use KernelTestBase?
All of the following conditions must be true:
- If your test does not need the internal browser and web access to the Drupal site.
- If you are testing the expected behavior of a particular class or function only.
- If you tried hard to use UnitTestBase, and only had to use WebTestBase, because of errors and exceptions being caused by missing module/extension/cache/lock services.
- If you need to enable or even install a module in order to test a function of it (e.g., to install its configuration or database tables).
- If the functionality you are testing cannot be properly injected into a unit test.
When is it NOT appropriate to use KernelTestBase?
In case any of the following conditions are met:
- If you can properly inject all dependencies with mocked services into a unit test.
- If you need the internal browser to perform requests. (e.g., drupalGet(), drupalPost())
- If the functionality you are testing could behave differently in a fully installed Drupal environment.
Usage
KernelTestBase shares some aspects with WebTestBase and provides custom methods for enabling modules and installing a database schema:
-
::$modules
allows to specify a list of modules to load. The specified modules are only loaded and "enabled" as a so called "fixed" (locked) module list, but not installed:/** * Modules to enable. * * @var array */ public static $modules = array('config_test');
-
$this->enableModules()
allows to enable additional modules:$this->enableModules(array('mymodule_test'));
Important:
module_enable()
cannot be used directly. Always use the test helper method$this->enableModules()
. -
$this->installSchema()
allows to install database table schema:// Install System module's {queue} database table schema. $this->installSchema('system', 'queue'); // Multiple tables from the same module can be installed at once. $this->installSchema('system', array('queue', 'keyvalue'));
-
$this->installConfig()
allows to install the default configuration of a module:// Install default configuration from filter.module. $this->installConfig('filter');
-
$this->render()
allows to render a render array + using content assertion methods:$build = array( '#markup' => 'Foo', ); $this->render($build); $this->assertText('Foo');
-
$this->containerBuild()
allows to add or override service definitions in the base dependency injection container that is used whenever the DrupalKernel is updated/rebuilt:class MyTest extends KernelTestBase { protected function containerBuild() { global $conf; parent::containerBuild(); // Replace the keyvalue service with the in-memory implementation. $conf['keyvalue_default'] = 'keyvalue.memory'; $this->container ->register('keyvalue.memory', 'Drupal\Core\KeyValueStore\KeyValueMemoryFactory'); } }
Note: The above example is set up by KernelTestBase::containerBuild() already; you do not need to duplicate that.
Note: If your test does not use
enableModules()
, then you most likely do not need to usecontainerBuild()
.