Skip to content

Instantly share code, notes, and snippets.

@dotherightthing
Last active November 1, 2018 10:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dotherightthing/c8dede3241909e931e62deaf3acf7b8b to your computer and use it in GitHub Desktop.
Save dotherightthing/c8dede3241909e931e62deaf3acf7b8b to your computer and use it in GitHub Desktop.
[PHP Unit Testing, revisited] #php #unit-testing #wordpress

PHP Unit Testing, revisited

See latest setup notes here: dotherightthing/generator-wpdtrt-plugin-boilerplate#28


When I last tried to set up Unit Testing, I ran into a suite of errors, but that was in a multi-site setup. This time I am using wptest, a standalone site which I set up just for testing plugins.

I'm following Pippin's tutorial on Unit Tests for WordPress Plugins – Setting Up Our Testing Suite.

As a side note, Pippin uses WP-CLI which outputs errors to the MacOS/Linux terminal. Some other tutorials use QUnit, which outputs errors to an HTML page. WP-CLI would fit better with my development workflow, while Qunit would be better for debugging issues in other users' hosting. For now, I'll use WP-CLI - baby steps!

1. Install PHPUnit and WP CLI

I am using Composer to manage my plugin's dependencies, so I add these dependencies to my plugin generator's require-dev object.

"require-dev": {
  "phpunit/phpunit": "^5.7",
  "wp-cli/wp-cli" : "~0.22",
  "psy/psysh" : "~0.6"
}

I verify that this worked by running wp --info:

$ wp --info
PHP binary:	/usr/bin/php
PHP version:	5.6.30
php.ini used:	
WP-CLI root dir:	phar://wp-cli.phar
WP-CLI packages dir:	
WP-CLI global config:	
WP-CLI project config:	
WP-CLI version:	1.1.0

but,

dsmac:wptest dan$ phpunit --version
PHPUnit 6.3.0 by Sebastian Bergmann and contributors.

This version of PHPUnit is supported on PHP 7.0 and PHP 7.1.
You are using PHP 5.6.30 (/usr/bin/php).

This is odd, because I know that I need PHPUnit 5 and composer.json specifies this (^5.7 is equivalent to >=5.7 <6.0.0)

  "require-dev": {
    "phpunit/phpunit": "^5.7",
    "wp-cli/wp-cli" : "~0.22",
    "psy/psysh" : "~0.6",
  },

Ok so let's remove this too-new version:

dsmac:wptest dan$ which phpunit
/usr/local/bin/phpunit
dsmac:wptest dan$ rm -r /usr/local/bin/phpunit
dsmac:wptest dan$ which phpunit
dsmac:wptest dan$ composer update

PHPUnit is now reinstalled, but isn't added to my $PATH.

$ phpunit --version
-bash: phpunit: command not found

I reckon that it should be possible to run phpunit from Composer's install location, perhaps like so:

$ ./vendor/phpunit/phpunit/phpunit --version

But that gives me an error about class-tgm-plugin-activation.php, which I already know about and work around in other ways. Short answer: too hard basket.

Looks like it's best to install the PHP Archive (.phar) package rather than trying to manage this dependency via Composer. I prefer to avoid global installations like this because they require more work on the part of the user. I'd like to smooth over those rough edges where possible.

$ wget https://phar.phpunit.de/phpunit-5.7.phar
$ chmod +x phpunit-5.7.phar
$ sudo mv phpunit-5.7.phar /usr/local/bin/phpunit

Testing that it worked:

$ phpunit --version
PHPUnit 5.7.25 by Sebastian Bergmann and contributors.

Great, both of our dependencies are now installed.

2. wp scaffold plugin-tests

After installation of dependencies, the next step is to generate the files needed for running PHPUnit tests in a plugin:

Expected result

The WordPress documentation for wp scaffold plugin-tests provides instructions and what to expect:

$ cd /path/to/wordpress/root
$ wp scaffold plugin-tests plugin-name
Success: Created test files.

Actual result

$ cd /path/to/wordpress/root
$ wp scaffold plugin-tests plugin-name
$ Warning: mysqli_real_connect(): (HY000/2002): No such file or directory in /Volumes/DanBackup/Websites/wptest/wp-includes/wp-db.php on line 1603
Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in /Volumes/DanBackup/Websites/wptest/wp-includes/wp-db.php on line 1633
Warning: mysql_connect(): No such file or directory in /Volumes/DanBackup/Websites/wptest/wp-includes/wp-db.php on line 1633
Error: Error establishing a database connection. This either means that the username and password information in your wp-config.php file is incorrect or we can’t contact the database server at localhost. This could mean your host’s database server is down.

Aiiieeee! I must admit than when I see a multiline error message in Terminal, my first reaction is to copy and paste it into Google. But since the smart people who built this complex software took the time to provide error messages, I should at least show my appreciation by attempting to read it.

Let's have a closer look.

Troubleshooting connection errors (mysqli_real_connect(), mysql_connect(), wp-config)

I check the MAMP Pro icon. It is blue and MySQL is definitely running.

Google suggests granting network access to MAMP Pro:

  1. MAMP Pro
  2. Main Window
  3. MySQL
  4. Allow network access to MySQL [check]
  5. Only from this Mac [check]

However after restarting MySQL and rerunning the command I still get the error.

Choosing between wp-config.php vs wp-tests-config.php (red herring)

Error: Error establishing a database connection. This either means that the username and password information in your wp-config.php file is incorrect or we can’t contact the database server at localhost. This could mean your host’s database server is down. DataBase connection problem with PHPUnit and WordPress offers this solution:

When running the PHPUnit tests, WordPress will load the config from wp-tests-config.php instead of wp-config.php. You need to add your database connection details in wp-tests-config.php.

And if you use MAMP or AMPPS to host your database, you will have to use 127.0.0.1 as DB_HOST instead of localhost.

I create this, following wp-tests-config-sample.php.

A couple of important points:

  • testing is destructive and needs to be done on a separate, empty database. I create one called wp_plugintests
  • DB_HOST has to be 127.0.0.1 as I use MAMP Pro

Let's try again.

Parse error: parse error in phar:///usr/local/bin/wp/php/WP_CLI/Runner.php(982) : eval()'d code on line 28
Error: Error establishing a database connection. This either means that the username and password information in your wp-config.php file is incorrect or we can’t contact the database server at DB_HOST. This could mean your host’s database server is down.
dsmac:wptest dan$ 

Testing the database connection

Let's check that the database connection is actually working:

Following How to: Connect to my MySQL Database server using command line and php, but using the MySQL path found in MAMP Pro > Main Window > MySQL, let's get a mysql prompt:

/Applications/MAMP/Library/bin/mysql -u dan -h 127.0.0.1 -p

This prompts me for a password, and I enter DB_PASSWORD from wp-tests-config.php.

Now, what commands are available?

mysql>\h
...
connect   (\r) Reconnect to the server. Optional arguments are db and host.

This is better explained in the MySQL documentation:

connect [db_name host_name]], \r [db_name host_name]]

Reconnect to the server. The optional database name and host name arguments may be given to specify the default database or the host where the server is running. If omitted, the current values are used.

Ok, so using the DBNAME and DB_HOST details in wp-tests-config.php:

mysql> connect wp_plugintests 127.0.0.1
Connection id:    367
Current database: wp_plugintests

Ok, that definitely seems to be working. Ctrl+C to exit.

But wp scaffold plugin-tests plugin-name still generates an error...

Sacrificing the site database

Ok, so as wp-tests-config.php is not being picked up, we'll have to use wp-config.php which means sacrificing the wp_test database just to get this thing up and running:

// wp-config.php
define('DB_NAME', 'wp_test'); // this is the test site database, rather than a separate database for unit testing
define('DB_HOST', '127.0.0.1'); // this resolves the connection issues	 

Ok, now we're making progress:

Make sure the plugin to be tested is active

dsmac:wptest dan$ wp scaffold plugin-tests wpdtrt-testplugin1
Error: Invalid plugin slug specified.

Hmm, but:

$ wp plugin list

Ah, my plugin is inactive

$wp plugin list
Fatal error: Call to undefined function cli\strwidth() in /Volumes/DanBackup/Websites/wptest/wp-content/plugins/wp-testplugin1/vendor/wp-cli/php-cli-tools/lib/cli/Colors.php on line 200

Grrr, but

wp scaffold plugin-tests wp-testplugin1
Success: Created test files

Yay! We'll come back to that cli\strwidth() issue later.

Running the demo test

Next we run the shell script which has been installed in wp-testplugin1/bin/.

Note that this script creates a database with the DB_NAME that we've provided, which will wipe out whatever is there already.

$ cd wp-content/plugins/wp-testplugin1
$ bash bin/install-wp-tests.sh wordpress_test wp_plugintests 123456789 127.0.0.1 latest
$ phpunit

But specifying a new database doesn't work [TODO ADD DETAILS], so we'll come back to this too.

$ cd wp-content/plugins/wp-testplugin1
$ bash bin/install-wp-tests.sh DB_NAME DB_USER DB_PASSWORD DB_HOST latest
$ phpunit

From https://make.wordpress.org/cli/handbook/plugin-unit-tests/#running-tests-locally, the arguments are:

  1. DB_NAME is the name of the test database (wp_plugintests) (all data will be deleted!)
  2. DB_USER is the MySQL user name (dan)
  3. DB_PASSWORD is the MySQL user password (secret!)
  4. DB_HOST is the MySQL server host (127.0.0.1)
  5. latest is the WordPress version; could also be 3.7, 3.6.2 etc.

Running the script outputs this:

+ install_wp     
+ '[' -d /tmp/wordpress/ ']'     
+ return     
+ install_test_suite     
++ uname -s    
+ [[ Darwin == \D\a\r\w\i\n ]]     
+ local 'ioption=-i .bak'    
+ '[' '!' -d /tmp/wordpress-tests-lib ']'    
+ '[' '!' -f wp-tests-config.php ']'     
+ download https://develop.svn.wordpress.org/tags/4.9/wp-tests-config-sample.php /tmp/wordpress-tests-lib/wp-tests-config.php
++ which curl    
+ '[' /usr/bin/curl ']'    
+ curl -s https://develop.svn.wordpress.org/tags/4.9/wp-tests-config-sample.php    
++ echo /tmp/wordpress/    
++ sed 's:/\+$::'    
+ WP_CORE_DIR=/tmp/wordpress/    
+ sed -i .bak 's:dirname( __FILE__ ) . '\''/src/'\'':'\''/tmp/wordpress//'\'':' /tmp/wordpress-tests-lib/wp-tests-config.php     
+ sed -i .bak s/youremptytestdbnamehere/wp_plugintests/ /tmp/wordpress-tests-lib/wp-tests-config.php     
+ sed -i .bak s/yourusernamehere/dan/ /tmp/wordpress-tests-lib/wp-tests-config.php     
+ sed -i .bak s/yourpasswordhere/123456789/ /tmp/wordpress-tests-lib/wp-tests-config.php     
+ sed -i .bak 's|localhost|127.0.0.1|' /tmp/wordpress-tests-lib/wp-tests-config.php    
+ install_db
+ '[' false = true ']'     
+ PARTS=(${DB_HOST//\:/ })     
+ local PARTS    
+ local DB_HOSTNAME=127.0.0.1    
+ local DB_SOCK_OR_PORT=     
+ local EXTRA=     
+ '[' -z 127.0.0.1 ']'     
++ echo    
++ grep -e '^[0-9]\{1,\}$'     
+ '[' ']'    
+ '[' -z ']'     
+ '[' -z 127.0.0.1 ']'     
+ EXTRA=' --host=127.0.0.1 --protocol=tcp'     
+ mysqladmin create wp_plugintests --user=foo --password=12345789 --host=127.0.0.1 --protocol=tcp

but ends with

bin/install-wp-tests.sh: line 122: mysqladmin: command not found

mysqladmin: command not found

I can see that DB_SOCK_OR_PORT is empty, but specifying a DB_HOST of 127.0.0.1:3006 doesn't help.

I know from wayyy back at the beginning that mysql is actually located at /Applications/MAMP/Library/bin/mysql. Perhaps the shell script is confused about where mysql lives.

Solution: Amend Bash Profile

Ok, so let's add MAMP Pro's copy of MySQL to our bash profile $PATH. This tells the Bash application to look in this folder for binaries which match the commands that we are issuing, such as mysql and mysqladmin. For more information about Bash, see Bash Profile.

$ nano ~/.bash_profile
export PATH="/Applications/MAMP/Library/bin:$PATH"
$ source .bash_profile

Now when we check the value of PATH we should see MAMP Pro's MySQL folder in there:

$ echo $PATH
/Applications/MAMP/Library/bin:/Users/dan/.rvm/gems/ruby-2.4.0/bin:/Users/dan/.rvm/gems/ruby-2.4.0@global/bin:/Users/dan/.rvm/rubies/ruby-2.4.0/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Users/dan/.rvm/bin:/Users/dan/.rvm/bin:/Users/dan/.rvm/bin:/Users/dan/.rvm/bin

Great.

Let's try the install again:

$ cd wp-content/plugins/wp-testplugin1
$ bash bin/install-wp-tests.sh DB_NAME DB_USER DB_PASSWORD DB_HOST latest
$ phpunit

Hooray! The install script runs without errors, and the default test runs and passes:

+ install_wp
+ '[' -d /tmp/wordpress/ ']'
+ return
+ install_test_suite
++ uname -s
+ [[ Darwin == \D\a\r\w\i\n ]]
+ local 'ioption=-i .bak'
+ '[' '!' -d /tmp/wordpress-tests-lib ']'
+ '[' '!' -f wp-tests-config.php ']'
+ download https://develop.svn.wordpress.org/tags/4.9/wp-tests-config-sample.php /tmp/wordpress-tests-lib/wp-tests-config.php
++ which curl
+ '[' /Applications/MAMP/Library/bin/curl ']'
+ curl -s https://develop.svn.wordpress.org/tags/4.9/wp-tests-config-sample.php
++ echo /tmp/wordpress/
++ sed 's:/\+$::'
+ WP_CORE_DIR=/tmp/wordpress/
+ sed -i .bak 's:dirname( __FILE__ ) . '\''/src/'\'':'\''/tmp/wordpress//'\'':' /tmp/wordpress-tests-lib/wp-tests-config.php
+ sed -i .bak s/youremptytestdbnamehere/wp_plugintests/ /tmp/wordpress-tests-lib/wp-tests-config.php
+ sed -i .bak s/yourusernamehere/dan/ /tmp/wordpress-tests-lib/wp-tests-config.php
+ sed -i .bak s/yourpasswordhere/123456789/ /tmp/wordpress-tests-lib/wp-tests-config.php
+ sed -i .bak 's|localhost|127.0.0.1|' /tmp/wordpress-tests-lib/wp-tests-config.php
+ install_db
+ '[' false = true ']'
+ PARTS=(${DB_HOST//\:/ })
+ local PARTS
+ local DB_HOSTNAME=127.0.0.1
+ local DB_SOCK_OR_PORT=
+ local EXTRA=
+ '[' -z 127.0.0.1 ']'
++ echo
++ grep -e '^[0-9]\{1,\}$'
+ '[' ']'
+ '[' -z ']'
+ '[' -z 127.0.0.1 ']'
+ EXTRA=' --host=127.0.0.1 --protocol=tcp'
+ mysqladmin create wp_plugintests --user=foo --password=123456789 --host=127.0.0.1 --protocol=tcp
Warning: Using a password on the command line interface can be insecure.
dsmac:wp-testplugin1 dan$ phpunit
Installing...
Running as single site... To run multisite, use -c tests/phpunit/multisite.xml
Not running ajax tests. To execute these, use --group ajax.
Not running ms-files tests. To execute these, use --group ms-files.
Not running external-http tests. To execute these, use --group external-http.
PHPUnit 5.1.3 by Sebastian Bergmann and contributors.

1 / 1 (100%)

Time: 2.63 seconds, Memory: 40.25Mb

OK (1 test, 1 assertion)

We can also see that the created several directories outside of our WordPress install:

  1. /User/private/tmp/wordpress. This looks like a standard WordPress install, including wp-config-sample.php
  2. /User/private/tmp/wordpress-tests-lib. This looks like a library of testing functions, and includes wp-tests-config.php. This matches the format of the one we manually created earlier, and is populated with the values which we passed to the shell script

Links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment