About drupal-unit
A Drupal unit-testing reference for PHP developers testing logic that does not depend on Drupal runtime services. It shows PHPUnit mocking patterns and commands for running module tests.
Key features
- 01Choosing unit tests for pure PHP
- 02PHPUnit mock examples
- 03Mocking Drupal translation
- 04Mocking logger expectations
- 05Commands for running module tests
Use cases
- 01Testing utility functions and transformations
- 02Testing classes with mocked dependencies
- 03Checking expected logger messages
- 04Running one module's unit test suite
Install
Copy the command and run it in a terminal.
$
npx skills add https:/ / github.com/ edutrul/ drupal-ai --skill drupal-unitAfter installing, type/drupal-unitin Claude Code to confirm it loaded.
Bundled filesREADME.md
Details
From the project READMEDrupal Unit Tests
When to Use
- Pure PHP logic with no Drupal dependencies
- Utility functions, data transformations, value objects
- Classes where all dependencies can be mocked
Basic Structure
// tests/src/Unit/MyServiceTest.php
namespace Drupal\Tests\my_module\Unit;
use Drupal\Tests\UnitTestCase;
use Drupal\my_module\Service\MyService;
use Psr\Log\LoggerInterface;
/**
* @coversDefaultClass \Drupal\my_module\Service\MyService
* @group my_module
*/
final class MyServiceTest extends UnitTestCase {
private MyService $service;
protected function setUp(): void {
parent::setUp();
$logger = $this->createMock(LoggerInterface::class);
$this->service = new MyService($logger);
}
/**
* @covers ::processData
*/
public function testProcessData(): void {
$input = ['foo' => 'bar'];
$result = $this->service->processData($input);
$this->assertArrayHasKey('processed', $result);
$this->assertTrue($result['processed']);
}
/**
* @covers ::processData
*/
public function testProcessDataWithEmptyInput(): void {
$this->expectException(\InvalidArgumentException::class);
$this->service->processData([]);
}
}
Mocking with PHPUnit
// Mock a simple interface
$mock = $this->createMock(SomeInterface::class);
$mock->method('someMethod')->willReturn('value');
// Mock with argument matching
$mock->expects($this->once())
->method('someMethod')
->with($this->equalTo('expected-arg'))
->willReturn('result');
// Mock throwing exception
$mock->method('someMethod')->willThrowException(new \RuntimeException('Error'));
// Stub multiple calls
$mock->method('someMethod')
->willReturnMap([
['arg1', 'result1'],
['arg2', 'result2'],
]);
Mocking Drupal Translation (t())
// UnitTestCase provides getStringTranslationStub()
$this->service = new MyService(
$this->getStringTranslationStub()
);
Mocking Logger
// Expect a specific log message
$logger = $this->createMock(LoggerInterface::class);
$logger->expects($this->once())
->method('error')
->with($this->stringContains('Failed to process'));
Running Tests
Examples assume a docroot/-based Drupal project. If your project uses web/ or another document root, adjust paths accordingly.
# Run specific test file
ddev exec vendor/bin/phpunit docroot/modules/custom/my_module/tests/src/Unit/MyServiceTest.php
# Run all unit tests for a module
ddev exec vendor/bin/phpunit docroot/modules/custom/my_module/tests/src/Unit/
# Run with verbose output
ddev exec vendor/bin/phpunit --verbose docroot/modules/custom/my_module/tests/
Comments
Markdown supported