How to run PHPUnit tests efficiently in Drupal 10/11?

23 / Apr / 2025 by Priya Roy Chowdhury 0 comments

Introduction

Do you know how important writing test cases for your functionalities you are developing is? Drupal always supports and gives an easy way to write test cases because it’s a vital part of development. It always helps in to early detection of bugs before deployment and also helps us to understand the code structure in a better way and the understanding of the business use cases.

In this article, we will understand How Efficiently we can run a test case using the PHPUnit framework in Drupal 10 and Drupal 11. Also, we will explore all the steps starting with installing the PHPUnit module via Composer, then we will understand what the necessary changes are in its configuration file, where we should locate this file, and then we will check a working example of writing a test case and how many ways we can run our test case.

unittest case in drupal

PHPUnit in Drupal

Here is the step-by-step process to write and run PHPUnit test cases in Drupal:

Step 1: Install PHPUnit in Drupal

Install the PHPUnit module via below command:

composer require --dev phpunit/phpunit

Executing this command adds PHPUnit to your project’s development dependencies.

Step 2:Configure the phpunit.xml file

PHPUnit.xml.dist is the default PHPUnit configuration file included with Drupal core. Just copy this file to the root directory of your Drupal project and rename it phpunit.xml to begin using it. The command for this is as follows:

If your Drupal 10/11 doesn’t have a web folder, then run below command:

cp core/phpunit.xml.dist phpunit.xml

If your Drupal 10/11 have a web folder, then run below command:

cp web/core/phpunit.xml.dist web/phpunit.xml

In this configuration file, you can define test suite directories, test files, bootstrap files, environment variables, and other settings.

Update this PHPUnit configuration file as per your requirement:

Add the module test class path as an example below. Here we have 2 types of test cases written, Unit Test cases and Functional Test cases. For each test type, you have to mention each module test class path.

Here, we have both Unit and Functional Test Cases. This can be any sort of test type based on your requirements. You need to mention the correct test class path, and mention under test type tags separately

NOTE: In the phpunit.xml file, under the <testsuites> tag, please remove unused test suites for any other tests, like functional/Kernel or any other if they have not currently been written, because if you keep those configurations, it will give you errors on running the whole test suite.

<phpunit bootstrap="core/tests/bootstrap.php"
         colors="true"
         verbose="true"
         stopOnFailure="false">
   <testsuites>
      <testsuite name="Custom Unit Tests">
        <directory>modules/custom/myexample_grlogger/tests/src/Unit</directory>
        <directory>modules/custom/string_matcher/tests/src/Unit</directory>
   </testsuite>
   <testsuite name="Functional Unit Tests">
        <directory>modules/custom/myexample_grlogger/tests/src/Functional</directory>
        <directory>modules/custom/string_matcher/tests/src/Functional</directory>
   </testsuite>
</testsuites>
</phpunit>

Also, confirm if the core/tests/bootstrap.php file exists, and the path of the bootstrap file must be as below :

<phpunit bootstrap="core/tests/bootstrap.php" 
 ...
<testsuites>
...

This will be the same in Drupal 10 and Drupal 11 and whether your project folder contains a web folder for core and other files, or there is no web folder in your Drupal project folder.

Step 3: Example: Create a custom module to write and run the test case:

We have taken an example of a use case where we need to enforce business rules for a service that compares two strings and logs messages in Drupal, creating a maintainable solution with PHPUnit-tested services, configurable discount tiers, and audit logging capabilities.

To help you understand the folder structure, here is an example of a file and folder structure.

PHPUnit in Drupal

Folder Structure

Step 4: Construct a custom Service

This service compares two strings and logs messages in Drupal.

File path: modules/custom/string_matcher/src/Service/StringComparerService.php

namespace Drupal\string_matcher\Service;

use Psr\Log\LoggerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;

class StringComparerService {

  protected LoggerInterface $logger;
  public function __construct(LoggerChannelFactoryInterface $logger_factory) {
    $this->logger = $logger_factory->get('string_matcher');
  }

 /**
  * Compares two strings and logs the result.
  */
  public function isEqual(string $a, string $b): bool {
    $result = $a === $b;
    $this->logger->info('Comparing strings: "@a" and "@b" => @result', [
      '@a' => $a,
      '@b' => $b,
      '@result' => $result ? 'Match' : 'No Match',
    ]);
  return $result;
  }
}

Step 5: Add the Service by *.service.yml config file

File: modules/custom/string_matcher/string_matcher.services.yml

services:
  string_matcher.comparer:
    class: Drupal\string_matcher\StringComparerService
    arguments: ['@logger.factory']

Step 6: Write a Unit test for the Drupal Service StringComparerService service

Writing the test for the custom service method is the last step. We’ll mock dependencies (if any) and test edge cases.

File: modules/custom/string_matcher/tests/src/Unit/StringComparerServiceTest.php

namespace Drupal\Tests\string_matcher\Unit;

use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\string_matcher\Service\StringComparerService;
use PHPUnit\Framework\TestCase;

class StringComparerServiceTest extends TestCase {

protected $loggerFactoryMock;
protected $loggerMock;
protected $service;

protected function setUp(): void {
  parent::setUp();
  $this->loggerMock = $this->createMock(LoggerChannelInterface::class);
  $this->loggerMock->expects($this->any())
   ->method('info');

  $this->loggerFactoryMock = $this->createMock(LoggerChannelFactoryInterface::class);
  $this->loggerFactoryMock->expects($this->any())
    ->method('get')
    ->with('string_matcher')
    ->willReturn($this->loggerMock);

  $this->service = new StringComparerService($this->loggerFactoryMock);
 }

 public function testStringsAreEqual() {
  $this->assertTrue($this->service->isEqual('hello', 'hello'));
 }

 public function testStringsAreNotEqual() {
  $this->assertFalse($this->service->isEqual('hello', 'world'));
 }
}

Step 7: Run the  Testcases using PHPUnit in Drupal 10/11

Run all commands below in the Drupal project root directory. If there is a web folder (maybe in Drupal 11) then run these commands outside web folder.

  1. Running the Test Suite

In Drupal 10, initiate the test runner with this terminal command

Here, in Drupal 10, we have used php8.2 and PHPUnit 9.5
./vendor/bin/phpunit

For Drupal 11, initiate the test runner with this terminal command

In Drupal 11, we are using php8.3,  PHPUnit 10.5+, for this we can run the above command in either ways below:

Command1 =>     ./vendor/bin/phpunit --configuration web/phpunit.xml
 
Command2  =>    ./vendor/bin/phpunit -c web/phpunit.xml

Without requiring you to specify specific test files, this will automatically find and run every test file you have indicated in your setup.

This command’s result is displayed below:

PHPUnit in Drupal

Run Test suite

2. Running a Specific Test File

Use this command to run tests from a specific file (such as one that has several test cases pertaining to a single service or class):

In Drupal 10, run below command for single file:

./vendor/bin/phpunit modules/custom/string_matcher/tests/src/Unit/StringComparerServiceTest.php
In Drupal 11, run below command for single file:

./vendor/bin/phpunit -c web/phpunit.xml web/modules/custom/string_matcher/tests/src/Unit/StringComparerServiceTest.php

The above command will run a single test file for all test cases. For this example, we have 2 test cases that will run by below command.

The outcome of this command is shown below:

PHPUnit in Drupal

Run by PHPUnit Single File

3. Running a Specific Test Method

If you need to run just a single method from a test class, you can make use of PHPUnit’s –filter option to do so.

In Drupal 10, use below command to run single function in a test file:

./vendor/bin/phpunit --filter testStringsAreEqual
In Drupal 11, use below command to run single function in a test file:

./vendor/bin/phpunit -c web/phpunit.xml --filter StringComparerServiceTest::testStringsAreEqual

This command will run a single testcase, and this helps save time when troubleshooting.

The outcome of this command is shown below:

PHPUnit in Drupal

Run by PHPUnit Single Function

Conclusion

Test Cases help in future code refactoring, new feature development, changes to existing features, early bug detection, faster development, and many more. A successful writing Unit Test in Drupal CMS primarily depends on how effectively you apply best practices and how much of your code is reusable, and how many scenarios are handled. This method of writing code allows you to create better tests with less work and achieve excellent outcomes.

FOUND THIS USEFUL? SHARE IT

Leave a Reply

Your email address will not be published. Required fields are marked *