Growing an object-oriented WordPress plugin guided by tests, part 1.
Allow people to run developer-provided scripts in the context of a WordPress installation without requiring knowledge or experience of the code.
The name “QA Thing” originates in the idea that the only possible scenario where people could be willing to run some PHP script written by others on their WordPress installation and thats’ the “itch” I’m “scratching” with the plugin.
I’ve read the book “Growing Object-Oriented Software Guided by Tests” some 2 and a half years ago and it contributed to shape the way I use Test-driven development; so much so that it’s among the books I commit to re-read every 2 years or so and the time and coincidence of this plugin development with it seems to be perfect.
I will read the book chapters as I go about the plugin development and document my personal, opinionated and biased point of view about development, test-driven development and, probably, WordPress.
The book is recommended for developers that know, at least, what testing is; the code examples are in Java but those are ablative enough that little extra “googling” of particular code structures is needed to understand them and all the building blocks of Object-oriented development are there.
Want a book recommendation? That’s it.
The book answers this question simply and brilliantly: with an acceptance test (also called “end to end” test).
This is a notion I hold dear: there are many examples of unit/integration testing in the WordPress echosystem and so many call to arms on the line “You should test your code” but few answer the question above in clear terms.
The testing environment setup process is one to be done just once but it’s where many give up so it’s probably worth detailing step by step.
The starting point is that I’ve got a bare bones version of the plugin setup but no testing environment for it; the plugin sits in my GitHub repositories folder and is symbolically linked in the WordPress development installation.
The WordPress development installation is handled with a Laravel Valet-like setup on my Mac:
- installed in the
~/Sites/wpfolder, vanilla WordPress file structure
- uses the
- is served by Apache at
- the plugin sits in
~/repos/qa-thingand is symlinked to the
The project PHP dependencies are managed using Composer so I will add
wp-browser as a development requirement:
cd ~/repos/qa-thing composer require --dev lucatume/wp-browser
After the installation is done I will bootstrap the testing suite:
Notice that I’m using the
wpcept command directly: that’s because my shell
PATH var is prefixed with the
vendor/bin relative path: that’s where Composer will put any package generated script file and it allows me to avoid installing dependencies globally.
Notice also that the command will output an error telling me that
/var/www/wordpress is not a valid WordPress installation path:
Nothing wrong here but it’s time to configure the scaffolded suites to work with my local setup.
Each but the
unit one will need to be configured to point to and fetch resources from the right places.
Starting from the acceptance suite configuration file and keeping the local set up shown above in mind:
class_name: AcceptanceTester modules: enabled: - \Helper\Acceptance - WPDb - WPBrowser config: WPDb: dsn: 'mysql:host=localhost;dbname=wp' user: root password: root dump: tests/_data/dump.sql populate: true cleanup: true url: 'http://wp.dev' tablePrefix: wp_ WPBrowser: url: 'http://wp.dev' adminUsername: admin adminPassword: admin adminPath: /wp-admin
WPDb module will bootstrap the WordPress installation before each acceptance test loading the
tests/_data/dump.sql file in the
wp database; while the database exists already (
wp-browser will not dare create databases for me) the dump is empty and I will create a good starting dump for the project using wp-cli:
cd ~/Sites/wp wp site empty --yes wp plugin deactivate $(wp plugin list --status=active --field=name) wp plugin activate qa-thing wp db export ~/repos/qa-thing/tests/_data/dump.sql
What’s happening? In plain English:
- remove any content from the website (and this is why real production sites should never be used for tests)
- deactivate all plugins
- activate just the
- export the resulting database to the dump file
To test that it’s working let’s try running the suite:
codecept run acceptance
functional suite requires little work:
class_name: FunctionalTester modules: enabled: - \Helper\Functional - Filesystem - WPDb - WordPress config: WPDb: dsn: 'mysql:host=localhost;dbname=wp' user: root password: root dump: tests/_data/dump.sql populate: true cleanup: true url: 'http://wp.dev' tablePrefix: wp_ WordPress: depends: WPDb wpRootFolder: /Users/Luca/Sites/wp adminUsername: admin adminPassword: admin
and since this suite was the one failing to build before I will rebuild Codeception modules before running it:
codecept build codecept run functional
The integration suite will not rely on the database fixture to setup the tests but will use the same system used by WordPress automated testing suite:
class_name: IntegrationTester modules: enabled: - \Helper\Integration - WPLoader config: WPLoader: wpRootFolder: /Users/Luca/Sites/wp dbName: wp-tests dbHost: localhost dbUser: root dbPassword: root tablePrefix: wp_ domain: wp.dev adminEmail: [email protected] title: WP Tests plugins: [qa-thing/qa-thing.php] activatePlugins: [qa-thing/qa-thing.php]
To note here:
- the only module used is the
- the module will access the database but I’m not pointing it to the
wpdatabase as I did for the acceptance and functional suites but to the
wp-testsone; this database does not exist and I’ve created it
- I’m telling the module to activate and include the
qa-thing/qa-thing.phpplugin; the fact that the plugin is symbolically linked there is of no consequence as WordPress will handle symbolic links like a champion
Again run the suite and see if it works:
codecept run integration
unit suite will require no configuration so it’s time to see if all the suites work together:
The afore-mentioned book underlines one of the key benefits of test-driven development: worrying about “what” is supposed to happen before “how” it’s supposed to happen.
The “what” here is better explained with a couple of user stories:
As a plugin developer part of the team I want to be able to add a script responsible for setting up a specific issue I' like QA to look into somewhere in the plugin folder And a configuration for the issue in the `qa/qa-config.json` file So that a QA person can apply the configuration (run the script) using a GUI As a QA person part of the team charged with testing a specific issue I want to be able to apply the issue configuration in my local test WordPress installation So that I can have the issue fixture set up for me exactly how it's meant to be
That’s essentially all the plugin is meant to do in its first iteration.
The user stories above are too wide to be used to write an acceptance test and I will split them into smaller scoped acceptance tests written using Codeception acceptance format.
Time to generate the first real acceptance test:
codecept generate:cept acceptance ConfigurationRead
Notice that the QA Thing plugin itself provides, at this stage, 3 valid configurations as a showcase in the
<?php $I = new AcceptanceTester( $scenario ); $I->am( 'a QA person' ); $I->wantTo( 'see plugin provided configurations in the QA options page' ); $I->loginAsAdmin(); $I->amOnAdminPage( '/admin.php?page=qa-options' ); // The QA Thing plugin itself provides them $I->canSeeNumberOfElements( '#qa-configuration option', 3 );
Now that I have a first failing acceptance tests is time to put in place, following along the book steps and flow, just enough code and tests to make the test pass.