Recently I set up Bitbucket Pipelines as CI for the DEITY Magento API module (background: DEITY: Technical Overview)
This was an interesting challenge, since I usually only run unit tests and integration tests on platforms like Bitbucket Pipelines, Travis CI or Wercker, and these do not need a functional web server. But for a module that only exists to extend the REST API, API functional tests are essential and the API functional test framework of Magento needs to make real HTTP requests using cURL.
Choosing a Magento Docker Image
With Bitbucket Pipelines you can choose any docker image as base image where the builds are executed. Additional docker images can be added as services (e.g. a database). It’s the same concept as in Wercker, which I like to use for Magento extensions hosted on Github.
I found the markoshust/magento-php image (previously “mageinferno/magento2-php”) image a good base for integration tests, together with a MySQL service. But for some reason the Bitbucket Pipeline scripts were never executed in the image. No error message, just “Build setup” and “Build teardown” without anything in between. I could not figure out why and how to solve it (if you have any idea, please let me know), but going back to the old mageinferno image worked, so here’s the main image:
1 |
image: mageinferno/magento2-php:7.1-fpm-0 |
Unlike the new one, this is missing a few packages to be able to install Magento and run tests within the container, so this goes into the script
block:
1 2 |
- apt-get update && apt-get install -y git mysql-client zip unzip libcurl4-openssl-dev - docker-php-ext-install curl |
For MySQL, the official image sometimes caused trouble with “MySQL server has gone away” errors during the Magento installation. It can be fixed by MySQL configuration (described in https://github.com/magento/magento2/issues/2805), but we cannot deploy custom MySQL configuration to the service, so I use our own image with adjusted configuration, integernet/mysql_ci.
In Bitbucket Pipelines, exposed ports of the services and the main image are automatically forwarded, so they can access each other at 127.0.0.1:$PORT
.
This is the setup that works for integration tests, but now we also need a web server. Naturally the first thing I tried is to add markoshust/magento-nginx as additional service. However, unlike with docker-compose, services in Bitbucket Pipelines or Wercker share a network, but cannot share volumes. All we can do to configure a service is setting environment variables, there’s no way to deploy code and configuration to the nginx container.
PHP Built-in Webserver
Luckily, there is a simple alternative: PHP contains a built-in webserver for development purposes, and Magento comes with configuration for it out of the box (see https://github.com/magento/magento2/tree/2.2-develop/phpserver).
It works well for the API tests. Start the web server after the Magento installation step, not immediately before running the API tests, otherwise they start before the web server is up and running. With the following command it starts in the background and listens on port 8082:
1 |
/usr/local/bin/php -S 127.0.0.1:8082 -t /var/www/html/pub/ /var/www/html/phpserver/router.php & |
Use http://127.0.0.1:8082/index.php/
as base URL in the Magento installation and in the phpunit.xml configuration for the api-functional test suite.
Pipelines Configuration
For reference, here’s the full bitbucket-pipelines.yml
configuration file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
image: mageinferno/magento2-php:7.1-fpm-0 pipelines: default: - step: name: Integration Tests caches: - composer script: - apt-get update && apt-get install -y git mysql-client zip unzip libcurl4-openssl-dev - docker-php-ext-install curl - ./bin/pipelines-test.sh services: - db definitions: services: db: image: integernet/mysql_ci:5.6 environment: MYSQL_DATABASE: magento MYSQL_ROOT_PASSWORD: magento |
I moved all commands for Magento installation and test execution into a separate script, bin/pipelines-test.sh
Script: Installation
Let’s walk through the script to install Magento with the module to test and to execute the tests.
First we set some environment variables:
1 2 3 4 5 6 7 8 9 10 11 |
#!/usr/bin/env bash set -e set -x export MODULE_DIR=`pwd` export MODULE_NAME=DeityApi export M2SETUP_VERSION=latest export M2SETUP_DB_HOST=127.0.0.1 export M2SETUP_DB_USER=root export M2SETUP_DB_PASSWORD=magento export M2SETUP_DB_NAME=magento |
The database credentials are the same as configured in bitbucket-pipelines.yml
. We need two additional environment variables so that composer can access the Magento repository: MAGENTO_REPO_PUBLIC_KEY and MAGENTO_REPO_PRIVATE_KEY. As repository admin in Bitbucket you can define private environment variables for pipelines in the project setting, so we added them there instead of in the repository.
Next, we create a temporary branch in the module repository, which is already checked out in the version that should be tested:
1 2 3 |
git checkout -b tmp git add -A git commit --allow-empty -m "tmp" |
We will refer to this branch later to install the module from the local repository with composer.
For the Magento installation we download the pre-built repositories from Nexcess (like the Mageinferno image in its install script), but you could also use composer create-project
. Afterwards, the Magento installation is finished with the setup:install
command:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
curl -L http://pubfiles.nexcess.net/magento/ce-packages/magento2-$M2SETUP_VERSION.tar.gz | tar xzf - -o -C /var/www/html/ cd /var/www/html /usr/local/bin/php ./bin/magento setup:install --db-host=${M2SETUP_DB_HOST} --db-name=${M2SETUP_DB_NAME} --db-user=${M2SETUP_DB_USER} --db-password=${M2SETUP_DB_PASSWORD} --base-url=http://127.0.0.1:8082/index.php/ --admin-firstname=Admin --admin-lastname=User --admin-email=dummy@example.com --admin-user=magento2 --admin-password=magento2 --language=en_US --currency=USD --timezone=America/New_York /usr/local/bin/php ./bin/magento deploy:mode:set developer |
Now it’s time to add our own module:
1 2 3 4 5 |
composer config http-basic.repo.magento.com ${MAGENTO_REPO_PUBLIC_KEY} ${MAGENTO_REPO_PRIVATE_KEY} composer config repositories.module vcs ${MODULE_DIR} composer require deity-core/deity-magento-api dev-tmp@dev /usr/local/bin/php ./bin/magento module:enable ${MODULE_NAME} /usr/local/bin/php ./bin/magento setup:upgrade |
First we add the Magento keys (if you use composer create-project
, you should have done this before). Then we tell composer where to find the local repository and install the module from the temporary branch. Finally the module is enabled and setup scripts are executed. This step is not necessary for integration tests which run on a separate database with all modules enabled by default, but for the functional tests it is.
Before we start the tests, we need to update the PHPUnit configuration for each test suite. The repository contains configuration files with some placeholders (like DB_HOST), we copy them over to the Magento installation and replace the placeholders:
1 2 3 4 5 6 7 8 9 10 11 12 |
cp ${MODULE_DIR}/tests/integration/phpunit.xml.dist /var/www/html/dev/tests/integration/phpunit.xml cp ${MODULE_DIR}/tests/integration/install-config-mysql.php /var/www/html/dev/tests/integration/etc/ cp ${MODULE_DIR}/tests/api-functional/phpunit.xml.dist /var/www/html/dev/tests/api-functional/phpunit.xml cp ${MODULE_DIR}/tests/api-functional/install-config-mysql.php /var/www/html/dev/tests/api-functional/config/ sed -i -e "s/DB_HOST/$M2SETUP_DB_HOST/g" /var/www/html/dev/tests/integration/etc/install-config-mysql.php sed -i -e "s/DB_USER/$M2SETUP_DB_USER/g" /var/www/html/dev/tests/integration/etc/install-config-mysql.php sed -i -e "s/DB_PASSWORD/$M2SETUP_DB_PASSWORD/g" /var/www/html/dev/tests/integration/etc/install-config-mysql.php sed -i -e "s/DB_NAME/$M2SETUP_DB_NAME/g" /var/www/html/dev/tests/integration/etc/install-config-mysql.php sed -i -e "s/DB_HOST/$M2SETUP_DB_HOST/g" /var/www/html/dev/tests/api-functional/config/install-config-mysql.php sed -i -e "s/DB_USER/$M2SETUP_DB_USER/g" /var/www/html/dev/tests/api-functional/config/install-config-mysql.php sed -i -e "s/DB_PASSWORD/$M2SETUP_DB_PASSWORD/g" /var/www/html/dev/tests/api-functional/config/install-config-mysql.php sed -i -e "s/DB_NAME/$M2SETUP_DB_NAME/g" /var/www/html/dev/tests/api-functional/config/install-config-mysql.php |
Find details about the test configuration files in the section “Test Configuration” below.
Script: Test
The second part of the pipelines-test.sh
script is actually running the tests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# # Start built in webserver # /usr/local/bin/php -S 127.0.0.1:8082 -t /var/www/html/pub/ /var/www/html/phpserver/router.php & # # Run integration tests # # Real path to phpunit executable must be used (not symlink in vendor/bin), otherwise composer autoloader is not found # cd /var/www/html/dev/tests/integration /usr/local/bin/php ../../../vendor/phpunit/phpunit/phpunit --log-junit ${MODULE_DIR}/test-reports/integration.xml # # Run api functional tests # # Real path to phpunit executable must be used (not symlink in vendor/bin), otherwise composer autoloader is not found # cd /var/www/html/dev/tests/api-functional /usr/local/bin/php ../../../vendor/phpunit/phpunit/phpunit --log-junit ${MODULE_DIR}/test-reports/api-functional.xml # # Production mode: test if compilation runs without errors cd /var/www/html /usr/local/bin/php ./bin/magento deploy:mode:set production |
Bitbucket Pipelines will pick up test logs from the test-reports
directory to display the summary of successful and failed tests.
Test Configuration
For the API functional tests, two configuration files are needed: install-config-mysql.php and phpunit.xml. For the MySQL configuration copy the original file from dev/tests/api-functional/install-config-mysql.php.dist
and change the following values (these are the placeholders that will be replaced by our script):
1 2 3 4 5 |
'db-host' => 'DB_HOST', 'db-user' => 'DB_USER', 'db-password' => 'DB_PASSWORD', 'db-name' => 'DB_NAME', 'base-url' => 'http://127.0.0.1:8082/', |
For the PHPUnit configuration, copy the original file from dev/tests/api-functional/phpunit.xml.dist
and change the test suite definition (i.e. remove the core test suite and add your own):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!-- Test suites definition --> <testsuites> <testsuite name="DeityApi Web API Functional Tests"> <directory suffix="Test.php">../../../vendor/deity-core/deity-magento-api/tests/api-functional</directory> </testsuite> </testsuites> <!-- Code coverage filters --> <filter> <whitelist> <!-- All CE modules --> <directory suffix=".php">../../../vendor/deity-core/deity-magento-api/src</directory> </whitelist> </filter> |
Adjust the paths within vendor
to match your own module and the location of its tests and source.
Also, change the TESTS_BASE_URL value:
1 |
<const name="TESTS_BASE_URL" value="http://127.0.0.1:8082/index.php/"/> |
For integration tests it’s almost the same.
What about Gitlab? Travis? [Insert your favorite build platform]?
The script can be used on any other platform, just the setup of the image and services might look differently. As mentioned before, Wercker works very similar, Gitlab Pipelines as well. For platforms that don’t allow building on a specific docker image and additional docker images as services, you will need a different setup to have a system where Magento can be installed: Basically that’s PHP 7 with the required PHP extensions and MySQL.

Author: Fabian Schmengler
Fabian Schmengler is Magento developer and trainer at integer_net. His focus lies in backend development, conceptual design and test automation.
Fabian was repeatedly selected as a Magento Master in 2017 and 2018 based on his engagements, active participation on StackExchange and contributions to the Magento 2 core.
Hi Fabian,
Just stumbled on this great writeup. Glad you are using the images I built! Perhaps we can get together and figure out a standard pipelines setup using the new docker images. A lot of changes happened to them in the last few months which could get them working well.
Cheers,
Mark
Thanks Mark! Sounds good, we could give it another try.
In other projects I’ve replaced the old mageinferno box with the one from meanbee for integration tests on CI, which has a php-cli version and otherwise is similar to yours. Tried your new php image but as it is based on php-fpm and that’s incompatible to Wercker (https://github.com/wercker/wercker/issues/262), I had to look into alternatives.
I did not work on this API testing stuff lately, but would try to run it on a php-cli image as well. Would you think about adding that as an option to yours?