A Docker container and template for testing individual Drupal modules with:
If you want to test a whole Drupal site, and not an individual module, see d8cidemo.
See this example repository using Drupal's node module for a live example of how this template is set up, and what sort of reports you will see in CircleCI for each job. The Elasticsearch Connector module is another good example using this template.
cd
to the directory with your Drupal module. Make sure it's a git
repository first!bash -c "$(curl -fsSL https://github.com/deviantintegral/drupal_tests/raw/master/setup.sh)"
COMPOSER_AUTH
environment variable to Circle if you are using
private repositories. See the composer documentation on COMPOSER_AUTH
for more details..circleci
directory. Then, in
the run step, copy the script to the root of the project. For example, if
you need to override hooks/code-sniffer.sh
, the run
step for the
code_sniffer
section would become:
- run:
working_directory: /var/www/html
command: |
cp ./modules/$CIRCLE_PROJECT_REPONAME/.circleci/code-sniffer.sh /var/www/html
./code-sniffer.sh $CIRCLE_PROJECT_REPONAME
If you ran setup.sh
these steps have been done automatically.
templates/module
to the root of
your new module.phpunit.core.xml.dist
and set the whitelist paths for coverage
reports, replacing my_module
with your module name.tests/src/Behat
, replace my_module
and MyModule
with your module name.$ composer require --dev --no-update \
cweagans/composer-patches \
behat/mink-extension:v2.2 \
drupal/drupal-extension:^4.0 \
bex/behat-screenshot \
phpmd/phpmd \
phpmetrics/phpmetrics
Unit, Kernel, Functional, and FunctionalJavascript tests all follow the same directory structure as with Drupal contributed modules. If the Drupal testbot could run your tests, this container should too.
Tests will be executed using run-tests.sh
. Make sure each test class has a
proper @group
annotation, and that base classes do not have one. Likewise,
make sure that each test is in the proper namespace. If a Unit test is in the
Kernel namespace, things will break in hard-to-debug ways.
Behat tests do not run on drupal.org, but we store them similarly. Most Behat
implementations are testing sites, and not modules, so their docs suggesting
tests go in sites/default/behat
don't apply. Instead, place tests in
tests/src/Behat
, so that you end up with:
tests/src/Behat
behat.yml
features/
my_module_settings.feature
bootstrap/
MyModuleFeatureContext.php
Behat can be buggy when using relative paths. To run your scenarios locally, run from the Drupal root directory with an absolute path to your configuration.
$ vendor/bin/behat -v -c $(pwd)/modules/my_module/tests/src/Behat/behat.yml
Behat is configured to use Selenium and Chrome along with a VNC server. If your CI provider allows SSH access to containers, you can forward ports to inspect the build.
In CircleCI, first rebuild the Behat job with SSH. Once you have the SSH command
to run, forward ports as needed. For example, to point port 8080
on your local
machine to Apache, and port 5900
to the VNC server, run:
$ <ssh command copied from the job> -L8080:localhost:80 -L5900:localhost:80
The container's site will now be available at http://localhost:8080
. To log
in to Drupal, use drush user-login
from SSH inside of the container.
$ cd /var/www/html
$ vendor/bin/drush -l localhost:8080 user-login
Click the link that is printed out and you should be logged in as administrator.
For VNC, connect to localhost:5900
with the VNC client of your choice. The
VNC password is secret
. If you manually run Behat tests from within the
SSH connection, you should see Chrome start and tests execute.
The phpunit.core.xml.dist
configuration file is copied to Drupal's core
directory before running tests. Feel free to edit this file in each module as
needed.
The coding standards job will run against the Drupal coding standard defined by the coder module. If you would like to customize the standard that PHPCS uses to check the code in your module, add a phpcs.xml.dist file to the root of your module. See the PHPCS docs for details on how to create such a file. An example file that's based on the Drupal standard, but has some overrides might look like this:
<?xml version="1.0"?>
<ruleset name="Drupal Tests Node Example" namespace="DrupalTestsNodeExample\CS\Standard">
<rule ref="vendor/drupal/coder/coder_sniffer/Drupal">
<exclude name="Drupal.Commenting.FunctionComment.MissingReturnComment" />
<exclude name="Drupal.Commenting.DocComment.MissingShort" />
</rule>
<exclude-pattern>*.jsx</exclude-pattern>
</ruleset>
Sometimes, a module needs to apply patches to Drupal or another dependency to
work correctly. For example, out of the box we patch Coder to not throw errors
on Markdown files. To add or remove additional patches, edit patches.json
using the same format as
composer-patches.
To update to the latest release of this template, simply run setup.sh
again.
Be sure to review for any customizations you may want to preserve. For example:
$ git checkout -b update-circleci
$ bash -c "$(curl -fsSL https://github.com/deviantintegral/drupal_tests/raw/master/setup.sh)"
$ git add -p # Add all changes you want to make.
$ git checkout -p # Remove any changes you don't want to make.
$ git status # Check for any newly added files.
$ git commit
In terms of semantic versioning, we consider the Docker image to be our "public" API. In other words, we will bump the major version (or minor pre-1.0) if updating the container also requires changes to the template files in a given module.
The Docker container builds against the stable branch of Drupal core, such as 8.9.x and not a specific release like 8.9.16. This helps ensure tests always run with the latest security patches. If you need to reproduce a build, see your build logs for the specific image that was used:
Status: Downloaded newer image for andrewberry/drupal_tests:0.0.3
using image andrewberry/drupal_tests@sha256:f65f0915e72922ac8db1545a76f6821e3c3ab54256709a2e263069cf8fb0d4e2
When a new minor version of Drupal is released:
Dockerfile
to point to the latest stable PHP release, such as
FROM php:7.3-apache
.ARG DRUPAL_VERSION_CONSTRAINT
in the Dockerfile
to reflect the
latest minor release. You could also pass this in as an argument to
docker build
in the next step using the --build-arg
flag.docker build -t drupal-8.9-test .
..circleci/config.yml
to with
-image: drupal-8.9-test
.circleci build --job run-unit-kernel-tests
and so on for
each job.config.yml
to point to it.