Tests can be run with vendor/bin/phpunit -c /app/phpunit.xml.dist /path/to/test
.
See Platform repository for an example.
See drupal/helfi_api_base module for an example.
By default, Drupal Core runs each test in a completely new Drupal instance, which is created from scratch for the test. In other words, none of your configuration and none of your content exists.
You can use the Drupal Test Traits (DTT) library to write tests that are run against an existing database.
- Install the library using Composer:
composer require weitzman/drupal-test-traits --dev
. - Make sure you have
tests/dtt/src/ExistingSite/
andtests/dtt/src/ExistingSiteJavascript/
folders under your git root - Register the
Drupal\Tests\dtt\
namespace by adding this to yourcomposer.json
:"autoload-dev": { "psr-4": { "Drupal\\Tests\\dtt\\": "tests/dtt/src" } }
- Modify your
phpunit.xml.dist
file and add these environment variables inside the<php>
section:and these<env name="DTT_MINK_DRIVER_ARGS" value='["chrome", {"browserName":"chrome", "goog:chromeOptions":{"w3c": true, "args":["--no-sandbox","--ignore-certificate-errors", "--allow-insecure-localhost", "--headless", "--dns-prefetch-disable"]}}, "http://chromium:4444"]'/> <env name="DTT_API_OPTIONS" value='{"socketTimeout": 360, "domWaitTimeout": 3600000}' /> <env name="DTT_API_URL" value="http://chromium:9222"/> <env name="DTT_BASE_URL" value="https://app"/>
<testsuite>
definitions under<testsuites>
section:<testsuite name="existing-site"> <directory>./tests/dtt/src/ExistingSite</directory> <directory>./public/modules/custom/*/tests/src/ExistingSite</directory> <directory>./public/modules/contrib/*/tests/src/ExistingSite</directory> </testsuite> <testsuite name="existing-site-javascript"> <directory>./tests/dtt/src/ExistingSiteJavascript</directory> <directory>./public/modules/custom/*/tests/src/ExistingSiteJavascript</directory> <directory>./public/modules/contrib/*/tests/src/ExistingSiteJavascript</directory> </testsuite>
You can find a couple of example DTT tests in drupal-helfi-etusivu repository.
See https://gitlab.com/weitzman/drupal-test-traits for more information.
Modify your phpunit.xml.dist
and add MINK_DRIVER_ARGS_WEBDRIVER
environment variable:
<env name="MINK_DRIVER_ARGS_WEBDRIVER" value='["chrome", {"browserName":"chrome", "goog:chromeOptions":{"w3c": true, "args":["--no-sandbox", "--ignore-certificate-errors", "--allow-insecure-localhost", "--headless", "--dns-prefetch-disable"]}}, "http://chromium:4444"]' />
- Make sure your
docker-compose.yml
file containschromium
service and theapp
service hasSIMPLETEST_BASE_URL: "https://app"
environment variable:services: app: chromium: image: selenium/standalone-chromium environment: SE_NODE_OVERRIDE_MAX_SESSIONS: "true" SE_NODE_MAX_SESSIONS: "16" SE_START_XVFB: "false" SE_START_VNC: "false" SE_SESSION_RETRY_INTERVAL: "1" SE_SESSION_REQUEST_TIMEOUT: "10" depends_on: - app networks: - internal profiles: - testing
- Start your local environment with
testing
compose profile. You can either modify your project's.env
file and appendtesting
toCOMPOSE_PROFILES
environment variable, or start the project withCOMPOSE_PROFILES=testing make up
.
In order for this to work, the chromium
container must be able to connect back to app
container, so $SIMPLETEST_BASE_URL
must be something that chromium
container can connect to.
-
The app container must be started using
--hostname
option:container: image: ghcr.io/city-of-helsinki/drupal-web:8.3-dev options: --hostname app --user 1001
-
Add
chromium
service to your actions yml:services: chromium: image: selenium/standalone-chromium env: SE_NODE_OVERRIDE_MAX_SESSIONS: "true" SE_NODE_MAX_SESSIONS: "16" SE_START_XVFB: "false" SE_START_VNC: "false" SE_SESSION_RETRY_INTERVAL: "1" SE_SESSION_REQUEST_TIMEOUT: "10"
-
You must start
nginx
andphp-fpm
services manually:# .github/workflows/yourworkflow.yml jobs: tests: steps: - name: Start services env: WEBROOT: ${{ env.DRUPAL_ROOT }}/public run: entrypoint &
You can find a complete example in City-of-Helsinki/drupal-module-helfi-navigation module.
- Copy backstop/ folder to your project
- Rename .github/workflows/visual-regression-test.yml.dist to
.github/workflows/visual-regression-test.yml
You can add/modify test scenarios/viewports in backstop/config.js file.
Generate reference images: node backstop/backstop.js reference
.
Run tests against reference images: node backstop/backstop.js test
.
The workflow works something like this:
- The reference images are generated on commit to
dev
branch and stored as Actions artifact. - Opening a pull request will then download the artifact, extract the images as reference, run tests against said reference images and compare what has changed.
- The HTML test result is uploaded to GitHub Pages, so you can visually preview the changes.
- Merging the pull request will mark the changes as “approved” and generate new reference images.
Tests are run in Docker container using your project's compose.yaml
file and Stonehenge.
Create an orphan gh-pages
branch if you don't have one already: git checkout --orphan gh-pages
. This will create a new branch with no parents, you can then clear the working directory with: git rm --cached -r .
.
Create and commit an empty .nojekyll
file. This will tell GitHub Pages to skip the build process since we're only deploying generated HTML content.
Go to your repository's Settings -> Pages and choose gh-pages
branch from the Branch
dropdown.
You must grant "Read and write" permissions to the GITHUB_TOKEN
so Actions can push to gh-pages
branch and trigger the deployment.
Go to your repository's Settings -> Actions -> General -> Workflow permissions and choose "Read and write permissions".
Create .github/workflows/visual-regression-cleanup.yml
file:
name: Delete old BackstopJS preview pages
on:
# This allows the workflow to be triggered manually from the Actions tab.
workflow_dispatch:
# Run once a day at 04:05.
schedule:
- cron: '5 4 * * *'
concurrency:
group: visual-regression
jobs:
visual-regression-cleanup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: gh-pages
- name: Setup Git user
run: |
git config --global user.name github-actions
git config --global user.email [email protected]
- name: Remove stale preview pages
env:
GH_TOKEN: ${{ github.token }}
# This will:
# - Loop through all preview folders (pull/{{ number }}) and parse the pull request number
# - Check if the pull request is still open using GitHub CLI tool.
# - Remove the folder if the pull request is not open
run: |
for d in pull/*; do
id=$(echo $d | cut -d / -f2)
state=$(gh pr view $id --json state --jq .state)
if [ "$state" != "OPEN" ]; then
rm -r pull/$id
fi
done
if [[ $(git status --porcelain) ]]; then
git add .
git commit -m 'Automated commit'
git push
fi