Automated testing for Drupal websites catches regressions before they reach production, speeds up development, and gives you confidence when updating modules or core. This guide walks you through setting up a testing pipeline using PHPUnit (the framework Drupal core uses) and Drupal’s built-in testing tools. You’ll learn how to write functional tests, run them locally, and integrate them into a CI workflow. No prior testing experience is required, but basic familiarity with the command line and Drupal administration helps.
What You’ll Need
- A local Drupal site (Drupal 9 or 10) installed via Composer, with a database and web server configured.
- Composer installed on your machine.
- PHP 7.4 or higher with the
sqliteextension enabled (for testing without a separate database). - Drush (optional but recommended) for running tests and managing the site.
- A code editor and basic knowledge of PHP syntax.
Step-by-Step
- Install PHPUnit via Composer. Drupal core already includes PHPUnit as a dev dependency. If it’s missing, run
composer require --dev phpunit/phpunit:^9.5in your project root. Verify withvendor/bin/phpunit --version. - Create a phpunit.xml file. Drupal provides a default configuration at
core/phpunit.xml.dist. Copy it to your project root:cp core/phpunit.xml.dist phpunit.xml. Edit it to set theSIMPLETEST_BASE_URLto your local site’s URL (e.g.,http://localhost) andSIMPLETEST_DBto a SQLite test database (e.g.,sqlite://localhost/tmp/test.sqlite). This tells PHPUnit where to run tests and store test data. - Set up the testing environment. Drupal tests run in an isolated environment. To enable this, create a
sites/simpletestdirectory and make it writable:mkdir -p sites/simpletest && chmod 777 sites/simpletest. Also, ensure yoursettings.phphas$settings['extension_discovery_scan_tests'] = TRUE;so test modules are discovered. - Write a basic functional test. In your custom module (e.g.,
web/modules/custom/my_module), create a directorytests/src/Functional. Inside, create a PHP class that extendsDrupal\Tests\BrowserTestBase. For example,MyModuleTest.phpwith a method that logs in as an admin and verifies your module’s page loads. Use the@groupannotation to categorize the test. - Run the test locally. From your project root, run
vendor/bin/phpunit --testsuite=functional --filter MyModuleTest. PHPUnit will spin up a test database, install Drupal, and execute your test. A green result means success. - Add a kernel or unit test (optional). For faster tests that don’t need a full browser, create
tests/src/Unitortests/src/Kernelclasses. Unit tests test individual functions; kernel tests test Drupal services with a minimal database. Use these for logic-heavy code. - Set up a Continuous Integration (CI) pipeline. Create a
.gitlab-ci.ymlor GitHub Actions workflow that runs PHPUnit on every commit. A simple CI job: install dependencies, runphpunitwith the same phpunit.xml, and fail if tests fail. This ensures every merge request is tested automatically. - Use Drupal’s testing module (Simpletest) for legacy tests. If you have older tests using the Simpletest API, enable the Simpletest module and run them via
drush simpletest-runor the UI at/admin/config/development/testing. New tests should use PHPUnit. - Test with a headless browser (optional). For JavaScript-heavy interactions, use
Drupal\Tests\FunctionalJavascript\WebDriverTestBasewith ChromeDriver. Install ChromeDriver, setSIMPLETEST_BASE_URLto your site, and run tests withvendor/bin/phpunit --testsuite=functional-javascript. - Document your testing setup. Add a
README.mdsection in your repo explaining how to run tests, so other developers can get started quickly.
Common Mistakes
- Forgetting to set
SIMPLETEST_BASE_URL– tests will fail with connection errors. Always double-check your phpunit.xml. - Using the wrong PHPUnit version – Drupal 9 requires PHPUnit 9, Drupal 10 uses PHPUnit 10. Mismatch causes fatal errors.
- Not clearing the test database – stale test data can cause false failures. Run
vendor/bin/phpunit --pruneor delete the SQLite file between runs. - Ignoring the
@groupannotation – without groups, you may run all tests, which is slow. Use groups to run only relevant tests. - Testing with production settings – never run tests against a live database; always use the isolated test environment.
Tips
- Start with a simple test that checks a page returns a 200 status. It validates your setup before writing complex tests.
- Use
drush phpunitif you have Drush – it sets environment variables automatically. - Run tests in parallel with
--parallelto speed up large suites, but be aware of resource usage. - Write tests for critical user journeys (login, content creation, checkout) – these give the most value.
- Keep tests independent: each test should work in isolation. Avoid relying on data from other tests.
Troubleshooting
- “Error: Class ‘Drupal\Tests\BrowserTestBase’ not found” – ensure you have the correct PHPUnit version and that your test file is in the right namespace and directory.
- Database connection errors – verify
SIMPLETEST_DBis correct and the SQLite extension is enabled. If using MySQL, create a test database and set the DSN accordingly. - Tests pass locally but fail in CI – CI often lacks a database or has different PHP settings. Ensure your CI job sets up a database and installs dependencies.
- “Maximum execution time exceeded” – increase PHP’s
max_execution_timein your php.ini or CI environment. - Tests hang on JavaScript – ensure ChromeDriver is running and the port is accessible. Update the
webdriver.chrome.driverpath in your phpunit.xml.
Frequently Asked Questions
Do I need a separate test database?
For local testing, it’s easiest to use SQLite, which creates a file-based database. In CI, you can use MySQL or PostgreSQL. The test runner creates a fresh database schema for each test, so you don’t need to maintain it.
Can I test my custom modules with PHPUnit?
Yes, place tests in your module’s tests/src directory. Drupal’s test discovery will find them as long as your module is enabled and the directory structure is correct.
How do I test forms and user interactions?
Use BrowserTestBase for standard form submissions (e.g., $this->drupalPostForm()). For AJAX or JavaScript interactions, use WebDriverTestBase with a headless browser.
What is the difference between functional and kernel tests?
Functional tests boot the full Drupal environment and simulate HTTP requests, which is slow but realistic. Kernel tests load a subset of services and use a minimal database, making them faster but less comprehensive.
How often should I run tests?
Run them at least before every commit, and automatically in CI on every push. This catches regressions early.
Can I test Drupal core’s functionality?
Yes, Drupal core has its own test suite. You can run it with vendor/bin/phpunit --testsuite=unit (for unit tests) or --testsuite=functional (for functional tests). This helps verify your environment is set up correctly.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Campus Tech Innovation Hubs: What They Are
- Poco C51 Airtel Prepaid Locked Variant India?
- Jenkins vs GitHub Actions: Which CI/CD Fits Your Team?
- Turn Off Backup Encryption in iTunes?
- Shared VPS Hosting: Is It Better?
- Disk Defrag Pro: What It Does and Whether You Need It