Skip to content

A Full Guide to PHPUnit in Drupal 10

Like many open-source projects, Drupal comes with automated tests that help prevent breaking changes while promoting code quality. Drupal 8 changed its testing framework for unit and functional tests from its own Simpletest framework to PHPUnit. PHPUnit is a unit testing framework hosted by GitHub and built for the PHP programming language.

The change from Simpletest to PHPUnit follows Drupal 8’s strategy of using more third-party libraries in Drupal Core. All Simpletests have been moved to PHPUnit as of Drupal 8.7.x, and the Simpletest module was removed in Drupal 9.

PHPUnit can handle different types of tests in Drupal core: unit tests, kernel tests, and functional tests. With these three tests, you can confirm the quality and reaction of code on edge cases in different layers.

Unit tests test functions, methods, and classes without requiring a database connection to run. On the other hand, kernel tests test the integration of module APIs and require a database connection to be configured to run. Functional tests test the entire system and require more setup than the others. Within the functional tests, there are both browser and JavaScript tests. In addition to these PHP-based tests, you may also run core JavaScript tests using the Nightwatch framework.

PHPUnit: Unit Tests

Your unit tests won’t be found unless they follow specific file structure, namespace, and metadata requirements. Drush can generate stub tests for a module.

When writing custom code, be sure to use dependency injection to make mocking easier. You can find detailed information in this blog post, or see the examples module.

Unit test mocks are created using Prophecy.

PHPUnit: Kernel Tests

Per the documentation of the KernelTestBase class, kernel tests are “useful for testing some types of integrations which don’t require the overhead of a fully-installed Drupal instance, but which have many dependencies on parts of Drupal which can’t or shouldn’t be mocked.” The class partially boots Drupal, and can load the services and hooks of other modules. The documentation says that the configuration, database schema, and entity schema could be installed for some modules, but it’s not clear which modules support this.

PHPUnit Functional Tests

Browser Tests

PHPUnit browser functional tests are run against a fresh Drupal installation so each test requires set up like installing modules, creating users, etc. This can be done in the BrowserTestBase::setUp() method (which is run before each test method in the class) or by writing an abstract base class (that extends BrowserTestBase) to be extended by additional test classes. You could also simply run a previous test at the beginning of the test you are writing. The results of the browser tests are saved to a directory, which can be configured in the phpunit.xml file. See PHPUnit Browser test tutorial in the Drupal documentation.

JavaScript Tests

PHPUnit Javascript functional tests are run against a fresh Drupal installation in an actual browser window, in order to test Javascript and AJAX functionality. They extend WebDriverTestBase. Running them requires setting up Chromedriver and Chrome/Chromium. See PHPUnit Javascript test writing tutorial in the Drupal documentation.

Behat Tests

While not part of Drupal core, some contributed modules also include tests using the Behat framework. Behat uses the Gherkin syntax to describe features, which are then translated to functional and UI tests.

Behat tests would be used to test site-specific functionality and uses the site’s database, rather than a clean installation of Drupal. The Behat Drupal Extension is an open-source project that provides drivers and useful step definitions for Drupal. Tests are organized into features, scenarios, and steps. Features and scenarios can be tagged to be run in groups, or to let Behat know to use a certain driver.

While this Gist offers a helpful list of step definitions provided by the Behat Drupal Extension, users can also run the command “behat -dl” for a list of steps defined for a system.

Each of these steps uses a different driver, which provides a different layer of integration with Drupal.

Blackbox Driver

The blackbox driver operates similarly to both the PHPUnit Browser and JavaScript functional tests. If the feature or scenario isn’t tagged, it will be run in a headless browser. If the test is tagged with @javascript, Behat can use Selenium to open a browser window and run the test. Selenium can then be set up to run the tests in several different browsers.

Drush Driver

The Drush driver uses the Drush command line tool to complete steps. In order to use the Drush driver, the feature or scenario must be tagged with @api. The Drush driver allows tests to use all the blackbox steps, plus a few more steps like logging in and creating users. The Drush driver can be run on multiple sites via Drush aliases.

Drupal API Driver

The Drupal API driver provides more complex steps, but can only be run on one site at a time. Behat can be configured to use this driver when features or scenarios are tagged with @api. Behat can be configured to run either the Drush driver OR the API driver at one time.

Custom Step Definitions

Custom step definitions would be added to the FeatureContext.php file that gets created when Behat is initialized for a project. The step text and any arguments are set up in annotations to functions in the FeatureContext class. See the Behat documentation for details.

Next Steps

Unit and functional tests are a great way to help improve your code quality. Drupal core supports many different kinds of tests that focus on different parts of the system, and some tests may be better for testing different things. Look for a future blog post on how to run these tests, and incorporate automated testing into your workflow.

Atlantic BT is well-versed in Drupal architecture, development, and testing. Learn more about our development services, or contact us to chat with an expert.

The Atlantic BT Manifesto

The Ultimate Guide To Planning A Complex Web Project