Tests are a large part of the project, both in terms of size (currently about 50% of the codebase) and importance. The test suite is evolving all the time but the basics are relatively stable so let's look at them.
In the early days of VersionPress, we didn't have a good way to answer a simple question "does it work at all?". Before every release, we just tried to click as many buttons in the wp-admin as possible and manually checked that the commits look right. It just didn't scale.
Sometime around 1.0, we decided to replace ourselves with Selenium and created what is now known as end2end tests (repo link). They are slow but very thorough – they execute WordPress actions and check that the Git commits are right and that the database is in sync with the repo. If these tests pass, we're pretty sure VersionPress does its work.
More recently, end2end tests were made more flexible to allow other type of "workers" beside Selenium, so for example now instead of clicking in the wp-admin, the tests can call WP-CLI commands to do the same actions. In the future, we will likely add REST API workers as well.
These end2end tests have the greatest value to us and are run before every release. At the same time, they have two downsides:
- They are slow and can easily take 5-15 minutes, depending on hardware.
- Some setup is required and for example Selenium is quite brittle when it comes to compatible versions of Firefox etc. (see below)
Which is why why have other types of tests as well:
- Unit tests cover small units of code. A great example are tests for IniSerializer, basically describing our core storage format. If there's a bug report, we create a unit test for it first and fix it second.
- Integration tests like StorageTests, SynchronizerTests or GitRepositoryTests. They touch external systems like the filesystem or the Git repo and are not as fast as unit tests but still much faster than full end2end tests.
We're currently working on a CI solution with the goal to run the slow but thorough end2end tests after every push to a feature branch on GitHub. It's not ready yet though so a local setup is required to run the tests.
To be able to run the tests you have to create a test config file. You can copy a sample from plugins/versionpress/tests/test-config.sample.yml
to test-config.yml
in the same directory. Update these values to match your local environment:
selenium
>firefox-binary
— path to your Firefox executable (see notes on Selenium below),common-site-config
,sites
— here you can specify a list of your test sites (or just simple alter thevp01
).
The test config file is generally very important as our WpAutomation depends on it and the tests use it to couldn't automate WordPress installation to run tests in it.
More detailed setup instructions follow, if you just want to run the tests skip to Running tests.
Selenium Server is downloaded automatically by the test runner script (tests/gulpfile.js
) but still requires some local env setup because:
- It requires Java. Make sure you have it installed and in your PATH.
- Compatibility with Firefox is unfortunately quite tricky. Currently, with Selenium Server 2.47.1 (see), Firefox 34 works. If we ever upgrade, you'll need to find out which version works :)
To set up a matching version of Firefox:
-
On a Mac, download Firefox in a matching version and put it somewhere. The path to this Firefox can then be put into
test-config.yml
like this:firefox-binary: /Users/johndoe/Path/To/Firefox.app/Contents/MacOS/firefox
-
On Windows, it's best to use Firefox Portable. To run it side-by-side with system Firefox, copy
FirefoxPortable.ini
fromOther/Sources
into the root and changeAllowMultipleInstances
to true.
On both systems, Selenium will always create a new Firefox profile so you don't need to worry that it will mess up with your default profile.
The test config sample is "Mac-first" so here's an example of what parts of the config might look like on Windows. Note the forward slashes.
selenium:
firefox-binary: C:/Path/To/FirefoxPortable/App/Firefox/firefox.exe
...
sites:
vp01:
host: localhost
db:
host: localhost
dbname: vp01
user: vp01
password: vp01
wp-site:
path: C:/wamp/www/vp01
url: http://localhost/vp01
title: "VP Test @ WampServer"
Other than different paths, all should be pretty much the same as on Mac.
The test config sample contains a couple of Vagrant configs that allow us to execute the whole test suite against various versions of PHP, Git etc. We do not use it much so feel free to skip this section, however, technically, here's the description of how to get it work.
Why we don't run Vagrant tests often: because they are very slow. They run the already slow end2end tests on virtual machines and interact with them via WP-CLI commands tunneled through SSH which, on Windows, is another emulated layer; you get the idea. We plan to replace this with cloud-hosted Docker machines with preset PHP / Git configurations which should be much faster.
Here's how to set up and run Vagrant tests:
- Install VirtualBox
- Install Vagrant
- Install vagrant-hostupdater - automatically updates the
hosts
file so that domains likevagrant-php53.local
work - Make sure
ssh
is in PATH (on Windows, add Git'sbin
directory to PATH) - Run the console as an admin
- Go to
versionpress/tests/vagrant
- Run
vagrant up
- This by default runs the
wordpress-php53
config. If you want another one run e.g.vagrant up wordpress-php55
- This by default runs the
- Create file
tests/wp-cli.local.yml
- you can copy it fromwp-cli.local.sample.yml
or this one - In the
test-config.yml
file, use one of the Vagrant sites as thetest-site
. - In the
tests
folder, rungulp run-tests
. - After you're done, run
vagrant halt
.
Vagrant troubleshooting:
Sometimes, the web server cannot see the WordPress installation - it just shows the default Apache page "It works". In such case, try to run vagrant provision
or vagrant halt
followed by vagrant up
.
Tests are run by PHPUnit which is automatically downloaded via Composer if you follow the Dev-Setup steps (there's no need to have PHPUnit installed globally).
To run all tests:
- Go to the
PROJ_DIR/plugins/versionpress/tests
directory. - Make sure the
test-config.yml
file is ready - Run
gulp run-tests [--force-setup]
.
The gulp task downloads Selenium Server, starts it and runs all the tests.
🚽 If you can't get Selenium working, change the
end2end-test-type
in test-config fromselenium
towp-cli
. WP-CLI tests are less thorough and don't cover as much as full-fledged Selenium tests but are faster to run and generally much less troublesome to set up.
To customize what tests run, you can either edit the phpunit.xml
file in the tests
folder or use other method to run the tests, e.g., PhpStorm.
This is especially useful:
- For unit tests
- If you want to debug some test
- If you want to run a specific end2end test, e.g.,
PostsTest
.
Just right-click on desired directory / class (tests
for all, tests/Unit
for unit tests etc.) and choose Run. (For End2End tests with Selenium worker, you need to start the Selenium server manually first.)
The gulp script is set-up to log the results into these locations:
- The console
- phpunit-log.format.txt, for example,
phpunit-log.tap.txt
,phpunit-log.testdox.txt
and possibly others. They are in thetests
folder.
Just a couple of hints / conventions:
- Tests should be part of a feature. Algorithmic code like parsing something MUST have unit tests. For larger features we might write end2end tests afterwards, after the feature stabilizes a bit. In such case, there's a new issue for it.
- Test classes end with
Test
, notTests
. This is so that PHPUnit can automatically find tests classes. - For test method names, we use convention no. 4 from this article. See also generating agile documentation.
- We prefer marking tests with the
@test
annotation, not via thetestXyz
prefix.
- We prefer marking tests with the
Random collection of possibly useful resources:
- Automated Testing in WordPress presentation, interesting from slide 80 above
- http://wptest.io/