We’re learning how to move faster with Drupal, moving changes from a dev’s laptop to production quickly while maintaining (if not increasing) quality. In part 1 of this Continuous Drupal series, we covered how to get up and running with Drupal using a modern toolset. In this post we’re going to continue where part 1 left off and bring our Drupal website into CircleCI. We’ll run QA tests on the site to make sure everything is running as expected, and optionally PHPUnit test for custom modules or themes we may have. Let’s dive in.

Now that we have a Drupal website managed with Git and Composer, we’re in a great position to use tools such as CircleCI, Behat, and PHPUnit to test our website automatically.

Testing a Drupal 8 Website With Behat

Behat allows the description of how certain UI-based features should work in a near-English language. If you’re familiar with the concept of “User Stories”, Behat lets us write user stories in code in a way that we can test automatically, called Behaviour-Driven Development.

Install Behat

We’re going to install Behat and related tools. We’ll want to be within the app directory inside our Drupal container to do this.

docker-compose ps  # to get our container name
docker exec -it continuousblog_drupal_1 bash
composer require --dev behat/behat
composer require --dev behat/mink
composer require --dev behat/mink-extension
composer require --dev drupal/drupal-extension

Create a behat.yml file:

default:
  suites:
    default:
      contexts:
        - FeatureContext
        - Drupal\DrupalExtension\Context\DrupalContext
        - Drupal\DrupalExtension\Context\MinkContext
  extensions:
    Behat\MinkExtension:
      goutte: ~
      selenium2: ~
      base_url: http://localhost/
    Drupal\DrupalExtension:
      blackbox: ~
      api_driver: 'drupal' 
      drupal: 
        drupal_root: 'web' 

Then we’re going to have Behat initialize and run -dl to see the definitions list. Being able to view this list confirms that everything installed correctly.

vendor/bin/behat --init
vendor/bin/behat -dl

Testing User Behavior

Our Behat tests will go in the /app/features/ directory in our container. We’re going to create a quick generic test, in order to make sure our system is working on demo just as the test workflow works.

Create the file /app/features/global.feature:

Feature: Test Features
	Some test scenarios to make sure the website is generally working.

@api
  Scenario: Run cron
    Given I am logged in as a user with the "administrator" role
    When I run cron
    And am on "admin/reports/dblog"
    Then I should see the link "Cron run completed"

Now we can run our Behat tests with:

vendor/bin/behat

Feature: Test Features                   
  Test scenarios to make sure the website is generally working.                      

  @api                                    
  Scenario: Run cron                                             # features/global.feature:4                                                                              
    Given I am logged in as a user with the "administrator" role # Drupal\DrupalExtension\Context\DrupalContext::assertAuthenticatedByRole()                              
    When I run cron                                              # Drupal\DrupalExtension\Context\DrupalContext::assertCron()                                             
    And am on "admin/reports/dblog"                              # Drupal\DrupalExtension\Context\MinkContext::visit()                                                    
    Then I should see the link "Cron run completed"              # Drupal\DrupalExtension\Context\MinkContext::assertLinkVisible()                                        

1 scenario (1 passed)                     
4 steps (4 passed)                        
0m1.25s (13.18Mb)

At this point, we have a basic example for using Behat to test Drupal. The next step is to hook all of this up to CircleCI. Before we do so, let’s save our progress with Git.

exit  # to exit the container
git status  # you should recognize everything outside of the */files/* directory
git add .
git commit -m "Configure Behat and first test."

CircleCI Configuration

Now we’re going to set our Drupal website up on CircleCI. If this is your first time using CircleCI, make your way through the Getting Started Doc first.

The CircleCI configuration file should be located at .circleci/config.yml and ours will look like this:

version: 2
jobs:
  build:
    docker:
      - image: cibuilds/drupal:latest
      - image: mariadb:10.2
        environment:
          MYSQL_DATABASE: drupal
          MYSQL_ROOT_PASSWORD: HorriblePassword
          MYSQL_ROOT_HOST: "%"
    environment:
      BLUEMIX_ORG: CircleCI
      BLUEMIX_SPACE: dev
    working_directory: /project/app
    steps:
      - checkout:
          path: /project
      - run:
          name: "Setup Database & Server"
          command: |
            echo "127.0.0.1      db" >> /etc/hosts
            sleep 20
            mysql -uroot -pHorriblePassword -h127.0.0.1 drupal < ../db/drupal-db-dump.sql
            service apache2 start
      - run:
          name: "Install Dependencies"
          command: |
            composer install
      - run:
          name: "Behat Tests"
          command: vendor/bin/behat

Instead of using Docker Compose, as we did in the previous blog post, we’re using CircleCI and its “Docker Executor” to create our build environment. We’re going to do a walkthrough of the CircleCI Configuration in part 3 of this series. For now, I just want to touch on the “Setup Database & Server” step.

Managing The Database

When managing multiple environments, you’re likely to have multiple database locations. One each for production, CI, staging, dev, etc. For certain environments (such as dev) you may get away with using a lightweight fake DB. Many times you’re going to need to work on real data. This is done by making copies (dumps) of the DB and importing them into other environments. The key is the direction the data flows.

Always Move DB Data Downstream

This is important enough that it warrants its own header. When moving copies of DB data around, always start by copying the data from production. Then you can import it into staging or somewhere else. You may even then move staging data to your local machine. This practice prevents data loss. If you start with a DB on your local machine, and move it to production, you may lose users, posts, and more that were created since the local dev DB was last created.

echo "127.0.0.1 db" >> /etc/hosts - Our Drupal settings file knows that the database hostname that we set previously is db. This line always that hostname to work within the CircleCI container.

Tracking DB Export

For this example, the directory at the root of our repo ./db was created to hold DB dumps in the .sql format. There’s a dump from mysqldump in there from our local DB. Once your website is in production, you’ll want to retrieve the DB dump from there. It’s common practice for sites with sensitive data to sanitize the DB before importing it from production to other environments. Especially when these additional environments might be less secure. If there’s still sensitive information in the DB dump, you may not want to track it with Git as we’re doing here.

Lastly, Hosting Our Drupal Website

In the 3rd part of this “Continuous Drupal” series, we’re going to find a place to host our Drupal website using Kubernetes, allowing us to scale the hosting infrastructure as our site becomes more and more popular.

Questions & Keeping This Post Updated

If you have questions, or something to add to this post, please let us know at CircleCI Discuss.