The testing framework PHPUnit has been added to Drupal 8. Simpletest is still supported but should only be used for web tests and DrupalUnitTest's that require a complete or partial Drupal environment. At the moment, only unit tests can be written with PHPUnit.
PHPUnit is the de-facto standard tool to write unit tests in PHP. See the official manual for extensive documentation and the much more useful handbook page.
Getting your tests picked up
While your classes live under modules/foo/src
your tests should live under modules/foo/tests/src/Tests
with a namespace Drupal\foo\Tests
. The class needs to have a @group
annotation as well and the class/filename must end with Test.php (or .phpt):
namespace Drupal\foo\Tests;
use Drupal\Tests\UnitTestCase;
/**
* @group something
*/
class FooTest extends UnitTestCase {
// some phpunit garbage
}
You can run these tests by running ./vendor/bin/phpunit -c core
. Note: PHPunit apparently ignores classes extended by other classes.
Getting started
A few hints to get started when being used to write Simpletest tests:
- Extend from
\Drupal\Tests\UnitTestCase
- Only classes are loaded, so functions from modules and include files don't work. Move simple cases to static methods on classes and/or use services and dependency injection with interfaces. That also means that
debug()
does not work and there's no replacement for it (obviously, since PHPunit is better). - Test classes are placed in the tests directory of a module or core/tests for Drupal core classes. Use the namespace Drupal\$module\Tests for modules and Drupal\Tests\Core|Component for core classes
- assertEqual() is called assertEquals()
- To run tests, run
./vendor/bin/phpunit
, you might want to limit to your group (--group Breakpoint
), see-h
for other options. You can also integrate it with IDE's like PHPStorm. Using the Simpletest UI is not recommended as it is not well integrated (and it never will be). Alternatively, from the Drupal root dir./vendor/bin/phpunit -c core --filter SomePhpUnitClassTest
.
- Use mocking where possible to limit a single test to a single class, see the linked documentation and the example below.
Example
namespace Drupal\Tests\Core\Entity;
use [...]
/**
* Tests the entity route access checker.
*
* @group Entity
*/
class EntityAccessCheckTest extends UnitTestCase {
/**
* Tests the method for checking access to routes.
*/
public function testAccess() {
$route = new Route('/foo/{var_name}', [], ['_entity_access' => 'var_name.update'], ['parameters' => ['var_name' => ['type' => 'entity:node']]]);
/** @var \Drupal\Core\Session\AccountInterface $account */
$account = $this->prophesize(AccountInterface::class)->reveal();
/** @var \Drupal\node\NodeInterface|\Prophecy\Prophecy\ObjectProphecy $route_match */
$node = $this->prophesize(NodeInterface::class);
$node->access('update', $account, TRUE)->willReturn(AccessResult::allowed());
$node = $node->reveal();
/** @var \Drupal\Core\Routing\RouteMatchInterface|\Prophecy\Prophecy\ObjectProphecy $route_match */
$route_match = $this->prophesize(RouteMatchInterface::class);
$route_match->getRawParameters()->willReturn(new ParameterBag(['var_name' => 1]));
$route_match->getParameters()->willReturn(new ParameterBag(['var_name' => $node]));
$route_match = $route_match->reveal();
$access_check = new EntityAccessCheck();
$this->assertEquals(AccessResult::allowed(), $access_check->access($route, $route_match, $account));
}
}