In my previous articles in this series, I showed how to structure a WordPress plugin using object-oriented PHP. Then I covered unit testing. Unit tests cover each component, totally isolated from all other parts of the system. Unit tests should not be concerned with how the object interacts with any dependencies including the database and WordPress.
Instead, once we have unit tests, we use integration tests to cover the effects of our code on the environment it is in. In general, we use phpunit for both types of tests. We just use them differently. We can think of phpunit as both a set of assertions we can use to write tests and a test runner we can use to run those tests or even other types of tests, for example, Behat tests.
Integration Testing With The WordPress Test Suite
The community project, varying vagrant vagrants (VVV) provides a system for creating local WordPress using Vagrant. Included in VVV is a site designed for developing WordPress core. The SVN repository it installs WordPress from also manages the source of the test suite.
This site can be used for running the WordPress test suite with plugins. If you open this site in your IDE, since it contains the WordPress test suite, you should get proper autocomplete for methods of the WP_UnitTestCase and other classes in the test suite.
For more resources, I covered setting up VVV in a previous Torque article. My friend Ahmad recently covered alternatives for local development in a different Torque post. You can also find the documentation for installing VVV here.
Setting Up The WordPress Test Suite With VVV
Once VVV is installed, you should be able to run a plugin, whose tests were created using the WP CLI command for scaffolding tests. I covered how to use this command to set up a plugin in an earlier article in this series.
In that plugin, such as the example plugin, I’ve been creating for this series — here is the commit before I switched to using Docker. From that plugin’s directory, open a shell and then switch to the bin dir:
cd bin
Then run the test install script:
bash ./install-wp-tests wordpress_unit_tests wordpress wordpress localhost
Then switch back to the plugin’s directory:
cd ../
Then you should be able to run the tests without any arguments:
phpunit
Setting Up The WordPress Test Suite With Docker
Vagrant used to be a very popular solution for local development. While some still use it, many developers are moving to Docker, since it is more portable. Docker makes it much simpler to have the same exact server for local development, local testing and automated testing using a CI/CD service such as Travis.
Personally, I have switched to Docker completely. It’s way more flexible and makes sharing development environments between Ubuntu, Windows and Mac simple. If you’re a Windows user, make sure you have Windows 10 Pro or higher and have hyper-v enabled.
Installing Docker and Docker basics is beyond the scope of this article. Honestly, you don’t need to really know anything about Docker, besides how to install it to get any of what I’m about to show you to work. That’s part of what is so cool about Docker. Here are the instructions to install Docker.
Try It With Gutenberg First
One of the many things I love about the Gutenberg project is it’s an opportunity to update what we use to make WordPress. For example, we’re now using React, Redux and other very powerful tools for building Javascript-driven interfaces. As a result, Gutenberg gives us a first-class reference of using React, Redux or other tool for developing WordPress plugins.
I’ve been following Gutenberg development so I can develop with Gutenberg, teach Gutenberg, but also to study and borrow how they use these new tools. I’m pretty obsessed with optimizing how we do all types of automated testing for Caldera Forms right now, so of course, I’ve been reading how it works in Gutenberg.
So, before I show you how to set up Docker to run the WordPress test suite for your plugin, let’s walk through how to install and use Gutenberg Docker environment and test suite. In the previous articles in this series, I’ve used Composer as the script runner. Gutenberg uses npm-scripts for the same reasons — making it easier to run exactly the same commands with exactly the same arguments every time you run a common task, such as code linting or unit tests.
Gutenberg’s test suite includes not only PHP tests, but also JavaScript unit tests, using the WordPress JavaScript test suite, and end to end tests. There is some really cool stuff there, but that’s way out of scope for this post. Don’t worry, I’ll cover automated testing for JavaScript later. Definitely, check out how it works in Gutenberg if you’re curious.
When you install Gutenberg through the WordPress plugin installer, you get a version optimized for use in production environments. This makes sense, it’s what most people who will use Gutenberg need.
You are not most people, delete it and start over. It’s going to take longer this way.
I’m assuming you have git installed, and know how to use it. If that assumption was incorrect, I apologize and offer to you this article I wrote about using Git, and this course on Git from Know The Code. Also, my friend Morten gave a really cool talk explaining the concept of git, for beginners at WordCamp US 2015 that I recommend.
Open up a shell — for example using OSX’s Terminal app — in your local WordPress site’s plugin directory. I use phpStorm, so I just drag the plugins directory into the terminal and it’s magically there.
Then you will need to clone the latest version of Gutenberg from Github.
git clone [email protected]:WordPress/gutenberg.git
Then, once it’s done, switch to the new directory
cd gutenberg
Now we have Gutenberg, but none of the JavaScript files have been built. We just have the source files, which get compiled using weback. Webpack is installed using npm and run using npm scripts. So first, install the npm dependencies:
npm install
Now we have everything we need to build the JavaScript client using npm:
npm run dev
Now we can setup the test suite by running the installer script:
bash ./bin/install-docker.sh
That should be it. You should be able to see the WordPress site that you just created with docker at http://localhost:8888/. You also should be able to run the unit tests with this command:
npm run test-php
You can slo run JavaScript unit tests:
npm run test-unit
You know have everything you need to contribute to Gutenberg and write tests to prove your contribution works as intended. You can learn more about Gutenberg test at: https://github.com/WordPress/gutenberg/blob/master/docs/testing-overview.md
Want To Create Your Own?
In Gutenberg, they put a lot of detail work into testing out of date versions of PHP and supporting other types of local environments.
If you want an example of how to use Docker and modern PHP for plugin development, I recommend example code by Chris Zarate and you can find the original code on Github. Gutenberg’s Docker configuration uses the chriszarate/wordpress/ container. You should be sensing a pattern here. I have added a few things from Gutenberg to Chris’ original example code, in my example plugin.
Here is the docker-composer.yml:
version: '3.1' services: wordpress: image: wordpress ports: - 8888:80 environment: WORDPRESS_DB_PASSWORD: example ABSPATH: /usr/src/wordpress/ volumes: - wordpress:/var/www/html - .:/var/www/html/wp-content/plugins/rest-api-search cli: image: wordpress:cli volumes: - wordpress:/var/www/html - .:/var/www/html/wp-content/plugins/rest-api-search mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: example MYSQL_DATABASE: wordpress_test wordpress_phpunit: image: chriszarate/wordpress-phpunit environment: PHPUNIT_DB_HOST: mysql volumes: - .:/app - testsuite:/tmp composer: image: composer volumes: - .:/app volumes: testsuite: wordpress:
Once we have our docker-compose.yml file, we can start the server with this command:
docker-compose -f docker-compose.yml up -d
That should show us a WordPress site at http://localhost:8888. If that’s working, we can add our tests with this:
docker-compose -f docker-compose.yml run --rm wordpress_phpunit /app/bin/install-wp-tests.sh wordpress_test root '' mysql_phpunit latest true`
That is expecting a file in bin/install-wp-tests.sh. I modified mine from the standard one I had before using code borrowed from Gutenberg.
With this in place, I can run the tests with this command:
docker-compose run --rm wordpress_phpunit phpunit
I’d never be able to remember all of that. That’s why I like Composer scripts so much. In previous articles in the series, I created composer scripts for other steps in the process. Instead of encapsulating all of those install steps, I borrowed the docker install script from Gutenberg.
Now, let’s add the new Composer scripts. Each script should encapsulate a step of the process. All of these relate to WordPress, so let’s prefix them with “wp-”. The first will be “wp-install”. This will call the new install script, saved at bin/install-docker.sh that sets up WordPress, updates the plugin’s Composer dependencies and installs the WordPress unit test suite. That’s everything we need to do before running the tests:
#!/bin/bash #Set WordPress version WP_VERSION=${1-latest} # Exit if any command fails set -e # Include useful functions . "$(dirname "$0")/includes.sh" # Check that Docker is installed if ! command_exists "docker"; then echo -e $(error_message "Docker doesn't seem to be installed. Please head on over to the Docker site to download it: $(action_format "https://www.docker.com/community-edition#/download")") exit 1 fi # Check that Docker is running if ! docker info >/dev/null 2>&1; then echo -e $(error_message "Docker isn't running. Please check that you've started your Docker app, and see it in your system tray.") exit 1 fi # Stop existing containers echo -e $(status_message "Stopping Docker containers...") docker-compose down --remove-orphans >/dev/null 2>&1 # Download image updates echo -e $(status_message "Downloading Docker image updates...") docker-compose pull --parallel # Launch the containers echo -e $(status_message "Starting Docker containers...") docker-compose up -d >/dev/null HOST_PORT=$(docker-compose port wordpress 80 | awk -F : '{printf $2}') # Wait until the docker containers are setup properely echo -en $(status_message "Attempting to connect to wordpress...") until $(curl -L http://localhost:$HOST_PORT -so - 2>&1 | grep -q "WordPress"); do echo -n '.' sleep 5 done echo '' # Install WordPress echo -e $(status_message "Installing WordPress...") docker-compose run --rm -u 33 cli core install --url=localhost:$HOST_PORT --title=TestSite --admin_user=admin --admin_password=password [email protected] >/dev/null # Check for WordPress updates, just in case the WordPress image isn't up to date. docker-compose run --rm -u 33 cli core update >/dev/null # If the 'wordpress' volume wasn't during the down/up earlier, but the post port has changed, we need to update it. CURRENT_URL=$(docker-compose run -T --rm cli option get siteurl) if [ "$CURRENT_URL" != "http://localhost:$HOST_PORT" ]; then docker-compose run --rm cli option update home "http://localhost:$HOST_PORT" >/dev/null docker-compose run --rm cli option update siteurl "http://localhost:$HOST_PORT" >/dev/null fi echo -e $(status_message "Server is running at:") echo -e $(status_message "http://localhost:$HOST_PORT") # Install Composer echo -e $(status_message "Installing and updating Composer modules...") docker-compose run --rm composer install # Install the PHPUnit test scaffolding echo -e $(status_message "Installing PHPUnit test scaffolding...") docker-compose run --rm wordpress_phpunit /app/bin/install-wp-tests.sh wordpress_test root example mysql "${WP_VERSION}" false >/dev/null echo -e $(status_message "Completed installing tests")
Then we need one to start the server. Let’s call it wp-start We don’t need bash script for that. It’s just:
docker-compose up -d
Then we need one to run the tests, it’s the same command as last I showed before, we’re just wrapping it in a composer script called wp-tests
docker-compose run --rm wordpress_phpunit phpunit
Now, the composer.json scripts looks like this:
{ "tests" : "composer fixes && composer sniffs && composer unit-tests && composer wp-tests", "unit-tests": "phpunit --testsuite=unit", "wp-install": "bash ./bin/install-docker.sh", "wp-start": "docker-compose up -d", "wp-tests": "docker-compose run --rm wordpress_phpunit phpunit --configuration phpunit-integration.xml.dist", "phpunit-v": "phpunit --version", "sniffs" : "phpcs src/ && phpcs tests/", "fixes" : "phpcbf src/ && phpcbf tests/", "lints" : "parallel-lint ./src --blame --exclude vendor && parallel-lint ./tests --blame --exclude vendor" }
Workshop At WordCamp EU 2018
If you want to know even more about unit testing, check out the workshop by Thorsten Frommen, Carl Alexander and Giuseppe Mazzapica at this year’s WordCamp Europe. These three are all Apex WordPress Developers and I would strongly recommend this workshop to anyone. I sure hope I can make it or the videos make it to WordPress.tv, which they should.
There are more details about this workshop here.
Moving Forward
In the next post in this series, I’ll cover writing integration tests with the WordPress PHP test suite. I’ll cover what integrations to test, keeping unit tests separate, using mocks, and what assumptions about WordPress and the database we can and can not make when writing these tests.
No Comments