Continuous Integration on Drupal Projects
In the Amazee Labs web maintenance team, we have many projects that range in Drupal version – from 6 to 9. Some we built and some we inherited from other teams. To make sure that any work we do going forward does not create any issues, we make changes to codebases all the time, because assuming that everything will play nicely together when deployed can lead to problems and that leads to more time being spent fixing bugs.
Using continuous integration (also known as CI) with your Drupal projects can be great for running various tests, code analysis, and maintaining standards, but CI can also help in other ways. The major benefit of CI is the ability to identify and resolve issues/problems before being moved on to production.
There are a few factors to consider when using CI, most importantly what will we be doing with it: Do we want to check coding standards? Run tests? Build for deployment? The next thing we should ask ourselves is what do we require for the automation to work: A production database? Just the code? Access to API endpoints?
In maintenance it can be any or all of these. Simple tasks like checking coding standards can be set up easily with GitHub or GitLab. These use their own built-in CI/CD systems and can be configured to scan code or even run tests on Drupal modules.
However, we have found that in order to get better security and run more complex tasks, Jenkins might be a better choice. Jenkins is an open-source automation server and it’s a great choice for automating parts of software development, and operations such as building, testing and deploying. For us, we can build exact local copies of any environment using Docker with Jenkins and run all the tests we like. This means that our tests are as life-like as possible, instead of running tests on example data we have created it will be an exact copy of what we find on the production site.
For maintenance reasons it is great to add tests to custom modules, so we can quickly find out when changes are made and if they have had an effect anywhere else in the code. Drupal has three main layers where tests can be run:
- Unit tests: They don’t require Drupal or a database to run so they’re fast and easy. However, they are a bit limited in some cases due to Drupal’s limited setup. Unit tests would be used when you want to pass a function some new data and want to confirm its results are as expected.
- Functional tests: These tests can be slow as it loads up Drupal and requires a database. They’re also good at testing a flow such as updating a field, although they can be affected by small things such as field or page name changes. They are nice and easy to write though.
- Kernel tests: These tests are a bit of a middle ground between the other two. They do require Drupal and a database, but instead of interacting with the frontend directly, APIs are used, which is faster than using functional tests and, in a lot of cases, can replace them.
For maintenance, having our tests run fast is great and the higher level tests more accurately recreate how users interact with our sites. As mentioned above, unit tests don’t require Drupal or a database so they are very speedy. Functional testing behaves more like how users would use the site, but as it uses a brand new database it is not exactly like the production site.
End-to-end testing is like functional testing except it uses a production environment which could be as simple as pointing your tests to the production URL and running them, but in our case, we found that we could recreate the production environment via CI and have all the tests run safely without worrying about adding any noise to the real environment. For this we use Cypress.io.
Setting up cypress and example
You can set up Cypress with any Drupal project and it can be done in two steps:
- In the root of your project run:
npm install cypressThis installs the Cypress dependency.
- Then run:
./node_modules/.bin/cypress openThis will open up Cypress and give you install example files to play with.
Now you have Cypress set up in your project and it is ready to configure tests!
A quick example of how easy it is to write tests with Cypress: The following code can be used with Drupal to check that the login page works as expected:
Other CI integration
In maintenance, the main thing we use CI for is to run automated tests. However, we have recently started to add other integrations such as: detecting bad coding practice/ code smell with CodeSniffers, checking that your code is following Drupal standards (https://www.drupal.org/docs/develop/standards/coding-standards) or in some cases compiling things such as decoupled frontends.
All of this could be done on third party CI platforms such as Travis, Circle, or CodeShip. Because these are paid services, they require zero maintenance from the users. As we have chosen to use Jenkins, it does mean we are maintaining and looking after our own service (no problem for the maintenance team though!) but, any of the other platforms are worth looking into.
Continuous integration is a big topic and there is so much you can do with it. From our perspective on the Amazee Labs maintenance team, we want to ensure that any changes we make are not having unknown effects across the site. While we maintain our own Jenkins server for building production environments in a safe container, this may not be for everyone and, depending on what you need to achieve, there are third-party services out there that can do the heavy lifting for you.
By identifying and targeting what exactly went wrong with a build and what steps can be taken to correct the error, CI Testing makes the development process seamless. Avoid unneeded development stress. Talk to our web maintenance team and learn about all the benefits of continuous integration today!