Testing Framework Overview
When to Use
Every Drupal module should include automated testing. Use this guide to understand the five testing frameworks Drupal provides and when each is appropriate.
Decision
| If you need... | Use... | Why |
|---|---|---|
| Test pure PHP logic with no Drupal dependencies | PHPUnit Unit Tests | Fastest execution, no database or container overhead |
| Test service integration with database and container | PHPUnit Kernel Tests | Minimal bootstrap, access to Drupal services |
| Test complete user workflows via HTTP | PHPUnit Functional Tests | Full Drupal installation, simulated browser |
| Test JavaScript and AJAX interactions | PHPUnit FunctionalJavascript Tests | Real browser with WebDriver, executes JavaScript |
| Measure performance impact and regression | Gander Performance Tests | OpenTelemetry tracing, performance budgets |
Pattern
// Core Drupal Testing Frameworks (5 Active)
// 1. Unit Tests - tests/src/Unit/
namespace Drupal\Tests\my_module\Unit;
use Drupal\Tests\UnitTestCase;
// 2. Kernel Tests - tests/src/Kernel/
namespace Drupal\Tests\my_module\Kernel;
use Drupal\KernelTests\KernelTestBase;
// 3. Functional Tests - tests/src/Functional/
namespace Drupal\Tests\my_module\Functional;
use Drupal\Tests\BrowserTestBase;
// 4. FunctionalJavascript Tests - tests/src/FunctionalJavascript/
namespace Drupal\Tests\my_module\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
// 5. Gander Performance Tests - tests/src/FunctionalJavascript/
namespace Drupal\Tests\my_module\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\PerformanceTestBase;
Note: Nightwatch.js is deprecated as of Drupal 9.4 and should not be used for new tests.
Common Mistakes
- Wrong: Using Functional tests when Unit/Kernel would suffice → Right: Use the simplest test type that validates your code (faster execution and easier maintenance)
- Wrong: Not testing at all → Right: Start with minimal Unit/Kernel coverage for core logic
- Wrong: Skipping JavaScript tests for AJAX features → Right: Use FunctionalJavascript for any AJAX or client-side interactions
- Wrong: Testing everything in one test method → Right: One test method per behavior, use
@dataProviderfor variations - Wrong: Not using
@dataProviderfor similar test cases → Right: DRY principle applies to tests too