Change record status: 
Introduced in branch: 

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.


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));

Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other updates done