How to run PHPUnit tests efficiently in Drupal 10/11?
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.

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.

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.
- 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:

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:

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:

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.