Running DTT based tests on GitLab CI

March 26, 2020

Tags: GitLab , Drupal Test Traits

It’s no longer necessary to expound upon the benefits of automated testing. It has been widely accepted as a standard mechanism to ensure software quality in a cost-effective manner over the long term. The only question is what kind of automated tests we should write. We won’t be dealing with that question in this article as that is a topic in itself. Here, we’ll look at one of the automated testing methods available to Drupal and how we integrate it with GitLab CI.

Writing automated tests for Drupal is harder than it is for many other systems. This is because any tests for a Drupal website may need significant database setup and even other systems (such as Solr, Elasticsearch, or even caching systems like Memcache or Redis.) Drupal itself is well tested using a wide array of tests at unit, functional, and system levels. Ergo, sites built with Drupal often don’t need tests for the underlying functionality, but on a functional level, which requires the entire Drupal setup. Typically, this means at least the database.

Fixtures

Commonly, this need for initial configuration and content required for tests is satisfied by what is called as fixtures in the testing world. These could be created as part of the test itself, or more commonly, as part of the setup method in the PHPUnit or Background section in Behat feature files. This is not too hard for simple configuration and content but gets exponentially harder when we need to add images.

This is where DTT shines. Specifically, the ExistingSite based methods provide the tools (aka, methods and traits) that help in testing sites with an existing database and content. The tradeoff here is that the team needs to maintain a test copy of the database, but this is a blessing in disguise. It is common that the testing team has test content for regression tests, and they are, more often than not, stored as documentation rather than an automated script. Maintaining a test database simplifies this process as well.

Testing

The testing flow is now straight-forward. Developers don’t have to think about how to create complex content structures or configuration via tests. This is not often worth testing as they are built on features provided by Drupal and/or contrib ecosystem, which is already tested there. Instead, developers can focus on just writing the assertions, which check for the behaviors essential to the customer.

There certainly are challenges to the infrastructure and the developer workflow. We are not creating content via tests, but we still need to import a database SQL file. There is also the problem of keeping this test database updated throughout the development lifecycle. We will explore these challenges in other articles. In this article, we will talk about configuring GitLab CI to run these tests.

Lastly, you may follow this flow to run any test. For instance, you could choose to import the database and run Behat based tests. That is beyond the scope of this article. If you’re interested in Behat, read our Automated Testing with Behat series.

DTT based tests

The rest of the article will assume you have followed the steps documented in DTT’s documentation to set up the tests in your codebase. Particularly, you have edited your phpunit.xml file to include existing-site and existing-site-javascript in the test suites section. You should be able to run your tests locally when you run PHPUnit as follows:

PHPUnit --configuration ./phpunit.xml --test suite existing-site

Since these are PHPUnit based tests, they are written using PHP. Read the DTT documentation for more details on this.

Configure GitLab CI

It is typical to configure GitLab the .gitlab-ci.yml file. In our CI pipeline, we will add a new job that will cover these steps.

  1. Build the environment required to run the site
  2. Setup the site database
  3. Setup and import configuration
  4. Run tests

Let’s look at each in more detail.

Site environment

GitLab CI jobs typically run in a Docker container, and you may specify the image in your .gitlab-ci.yml file. Run using a Docker image that provides an environment for Drupal sites. In our case, I use my image called hussainweb/drupal-base.

If the Docker image you use doesn’t include a database, then we also need to add a service. In our case, the Docker image we use doesn’t contain MySQL, and we add the image MariaDB:10.3 as a service for our job. If you’ve configured your GitLab runners using the Docker executor as we have, the service would be available on the host MariaDB.

Note: If you’re using Kubernetes executor for your GitLab runners, the services are always available on the localhost as of this writing. In the case of MySQL based services, you should instead use 127.0.0.1 as the host as using the localhost would switch to using a socket-based connection, which would fail. This is being fixed in GitLab with some caveats which are beyond the scope of this article (the MR has more details if you’re interested). Even after the “fix,” you would be able to access the service both via the hostname, such as MariaDB or 127.0.0.1.

Next, modify the server configuration (Apache in our case) to point to Drupal’s root directory. This can’t be set in the Docker image as the GitLab places the codebase in a specific directory. In our case, we have a utility shell script called ci.sh that does this for us. This script also sets up a settings.local.php file so that Drupal can access the database server. Make sure the settings.local.php is commented out in your settings.php, or modify the name and include it as per your preference.

We call this script in the before_script section of our job. See line 18 in our .gitlab-ci.yml file. The script itself is below.

Site database

Start by downloading and importing the database from a secure and accessible location. In our case, the drupal-base image doesn’t include MySQL CLI clients, and we have to install that as well. We do this in the before_script section of our job. See lines 15 to 17 in our .gitlab-ci.yml file.

In the long term, we would be dockerizing our databases, and we wouldn’t have to import them at all, which is one of the reasons I didn’t include the CLI inside the Docker image. We’ll cover that process in another post.

Setup

Run composer installs to bring in all the dependencies. You’re using a composer to manage your dependencies, aren’t you? At this point, you have everything necessary to run the site (both database and code). But your database might not be updated with recent changes in your code. Run config-import and cache-rebuild to make sure that configuration changes in the branch get imported. This is part of the script section of our job. See lines 21 to 26 in our .gitlab-ci.yml file.

Run the tests

You have everything now to run your tests. See line 29 in our .gitlab-ci.yml file.

You can view the entire Gist to adapt the code as per your needs. There are three files here, and you can adapt and move the supporting files (ci.sh and settings.local.php) wherever you want. I keep them both in the scripts/GitLab directory.

At the end of your CI run, you should see something like this.

DTT CI PHPUnit Output

Takeaways

Testing is important, and testing frequently is important. Automating tests and integrating it with CI systems such as GitLab is standard practice now. At Axelerant, we ensure we deliver solutions that pass through multiple checks to ensure that what we deliver meets our customer’s needs and enables happiness. Automated testing is just one tool we use to make this happen. Contact us to learn more about how we can help you.