Mike Van Winkle
www.mikevanwinkle.com ~ @mpvanwinkle
testing a 'unit' of work
testing a function
testing object method
All languages have libraries that help them unit test. PHP has PHPUnit.
curl -o /usr/local/bin/phpunit \
https://phar.phpunit.de/phpunit.phar
chmod +x /usr/local/bin/phpunit
phpunit
<?php
// returns the 'dog' from an input array
function get_dog_from_cat( $cat ) {
if ( is_array($cat)
AND array_key_exists('dog') ) {
return $cat['dog'];
}
return false;
}
<?php
class TestSomeFunctions extends PHPUNIT_Framework_TestCase {
function testDogFromCat() {
$test = array(
'cat' => 'Mabel',
'cat' => 'Tommy',
'dog' => 'Mike',
);
$this->assertEquals( 'mike', get_dog_from_cat($test) );
}
}
$> phpunit tests/test-test.php
PHPUnit 4.0.20 by Sebastian Bergmann.
E
Time: 49 ms, Memory: 2.50Mb
There was 1 error:
1) SomeFunctions::testDogFromCat
array_key_exists() expects exactly 2 parameters, 1 given
/srv/www/mikevanwinkle/tests/functions.php:5
/srv/www/mikevanwinkle/tests/test-test.php:11
FAILURES!
Tests: 1, Assertions: 0, Errors: 1.
vagrant@vvv:/srv/www/mikevanwinkle$
$> phpunit tests/test-test.php
PHPUnit 4.0.20 by Sebastian Bergmann.
.
Time: 31 ms, Memory: 2.75Mb
OK (1 test, 1 assertion)
WP-CLI
Dev Environment
Brain
Before
./src
./vendor
composer.json
meta_boxes.yaml
post_types_config.yaml
README.md
readme.txt
yaml-post-types.php
./bin
./src
./tests
./vendor
composer.json
meta_boxes.yaml
phpunit.xml
post_types_config.yaml
README.md
readme.txt
yaml-post-types.php
After
$> wp scaffold plugin-tests plugin-name
<phpunit
bootstrap="tests/bootstrap.php"
backupGlobals="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
>
<testsuites>
<testsuite>
<directory prefix="test-" suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>
phpunit.xml
$ bash bin/install-wp-tests.sh testdb testuser testpass
+ install_wp
+ mkdir -p /tmp/wordpress/
+ '[' latest == latest ']'
+ local ARCHIVE_NAME=latest
+ wget -nv -O /tmp/wordpress.tar.gz https://wordpress.org/latest.tar.gz
2015-03-25 15:58:18 URL:https://wordpress.org/latest.tar.gz [6186275/6186275] -> "/tmp/wordpress.tar.gz" [1]
+ tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C /tmp/wordpress/
+ wget -nv -O /tmp/wordpress//wp-content/db.php https://raw.github.com/markoheijnen/wp-mysqli/master/db.php
2015-03-25 15:58:18 URL:https://raw.githubusercontent.com/markoheijnen/wp-mysqli/master/db.php [87/87] -> "/tmp/wordpress//wp-content/db.php" [1]
+ install_test_suite
++ uname -s
+ [[ Linux == \D\a\r\w\i\n ]]
+ local ioption=-i
+ mkdir -p /tmp/wordpress-tests-lib
+ cd /tmp/wordpress-tests-lib
+ svn co --quiet https://develop.svn.wordpress.org/trunk/tests/phpunit/includes/
+ wget -nv -O wp-tests-config.php https://develop.svn.wordpress.org/trunk/wp-tests-config-sample.php
2015-03-25 15:58:19 URL:https://develop.svn.wordpress.org/trunk/wp-tests-config-sample.php [1374/1374] -> "wp-tests-config.php" [1]
+ sed -i 's:dirname( __FILE__ ) . '\''/src/'\'':'\''/tmp/wordpress/'\'':' wp-tests-config.php
+ sed -i s/youremptytestdbnamehere/testdb/ wp-tests-config.php
+ sed -i s/yourusernamehere/testuser/ wp-tests-config.php
+ sed -i s/yourpasswordhere/testpass/ wp-tests-config.php
+ sed -i 's|localhost|localhost|' wp-tests-config.php
+ install_db
+ PARTS=(${DB_HOST//\:/ })
+ local PARTS
+ local DB_HOSTNAME=localhost
+ local DB_SOCK_OR_PORT=
+ local EXTRA=
+ '[' -z localhost ']'
++ grep -e '^[0-9]\{1,\}$'
++ echo
+ '[' ']'
+ '[' -z ']'
+ '[' -z localhost ']'
+ EXTRA=' --host=localhost --protocol=tcp'
+ mysqladmin create testdb --user=testuser --password=testpass --host=localhost --protocol=tcp
mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'testdb'; database exists'
bin/install-wp-tests.sh
./tests
./
bootstrap.php
test-sample.php
<?php
class SampleTest extends WP_UnitTestCase {
function testSample() {
// replace this with some actual testing code
$this->assertTrue( true );
}
}
tests/bootstrap.php
<?php
$_tests_dir = getenv('WP_TESTS_DIR');
if ( !$_tests_dir ) $_tests_dir = '/tmp/wordpress-tests-lib';
require_once $_tests_dir . '/includes/functions.php';
function _manually_load_plugin() {
require dirname( __FILE__ ) . '/../yaml-post-types.php';
}
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
require $_tests_dir . '/includes/bootstrap.php';
$> 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 3.7.28 by Sebastian Bergmann.
Configuration read from /var/www/lilysinbloom/wp-content/plugins/yaml-post-types/phpunit.xml
.
Time: 835 ms, Memory: 25.25Mb
OK (1 test, 1 assertion)
phpunit
chmod -R 0755 tests/
<?php
class TestYamlfyInstance extends PHPUnit_Framework_TestCase {
public function testYamlfyInstance() {
$object = Yamlfy::instance();
$this->assertInstanceOf('Yamlfy',$object);
}
}
tests/test-yamlfy-instance.php
$ phpunit
Installing...
Running as single site... To run multisite, use -c tests/phpunit/multisite.xml
PHPUnit 3.7.28 by Sebastian Bergmann.
Configuration read from phpunit.xml
..
Time: 1.92 seconds, Memory: 25.50Mb
OK (2 tests, 2 assertions)
<?php
...
public function testVersion() {
$yamlfy = Yamlfy::instance();
$data = get_plugin_data( __DIR__.'/../yaml-post-types.php' );
$this->assertEquals( $yamlfy::version, $data['Version'] );
}
}
tests/test-yamlfy-instance.php
$ phpunit
Installing...
Running as single site... To run multisite, use -c tests/phpunit/multisite.xml
PHPUnit 3.7.28 by Sebastian Bergmann.
..F
Time: 830 ms, Memory: 25.75Mb
There was 1 failure:
1) TestYamlfyInstance::testVersion
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'0.1.0-alpha'
+'0.1-alpha'
tests/test-yamlfy-instance.php:13
FAILURES!
Tests: 3, Assertions: 3, Failures: 1.
$ phpunit
Installing...
Running as single site... To run multisite, use -c tests/phpunit/multisite.xml
PHPUnit 3.7.28 by Sebastian Bergmann.
...
Time: 1.21 seconds, Memory: 25.50Mb
OK (3 tests, 3 assertions)
public function init() {
try {
$parser = new \Symfony\Component\Yaml\Parser();
$finder = new \Symfony\Component\Finder\Finder();
if( !$configs = get_transient('yaml-post-types-configs') ) {
$dirs = apply_filters('yamlfy-config-dirs', array(dirname(__FILE__)));
$finder->name('*.yaml')->files()->in($dirs);
$configs = array();
foreach ( $finder as $file ) {
$configs[] = $parser->parse( $file->getContents() );
}
set_transient('yaml-post-types-configs', $configs);
}
foreach($configs as $config) {
$post_types = $taxonomies = $meta_boxes = $index = false;
if (array_key_exists('post_types', $config)) {
$this->register_post_types($config['post_types']);
}
if (array_key_exists('taxonomies',$config)) {
$this->register_taxonomies($config['taxonomies']);
}
if (array_key_exists('meta_boxes',$config)) {
$this->load_meta_boxes($config['meta_boxes']);
}
}
} catch (\Symfony\Component\Yaml\Exception\ParseException $e) {
$this->error("Could not parse {$file->getFilename()}:{$e->getMessage()}");
} catch(\Exception $e) {
$this->error($e->getMessage());
}
}
...
public function loadConfigs() {
$parser = new \Symfony\Component\Yaml\Parser();
$finder = new \Symfony\Component\Finder\Finder();
if( !$configs = get_transient('yaml-post-types-configs') ) {
$dirs = apply_filters('yamlfy-config-dirs', array(dirname(__FILE__)));
$finder->name('*.yaml')->files()->in($dirs);
$configs = array();
foreach ( $finder as $file ) {
$configs[] = $parser->parse( $file->getContents() );
}
set_transient('yaml-post-types-configs', $configs);
}
return $configs;
}
public function init() {
try {
$configs = $this->loadConfigs();
...
public function testConfigs() {
delete_transient('yaml-post-types-configs');
add_filter('yamlfy-config-dirs', function( $dirs ) { return array( __DIR__ ); });
$yamlfy = Yamlfy::instance();
$configs = $yamlfy->loadConfigs();
foreach ($configs as $config) {
$this->assertArrayHasKey('post_types',$config);
}
}
}
...
public function loadConfigs() {
$parser = new \Symfony\Component\Yaml\Parser();
$finder = new \Symfony\Component\Finder\Finder();
if( !$configs = get_transient('yaml-post-types-configs') ) {
$dirs = apply_filters('yamlfy-config-dirs', array(dirname(__FILE__)));
$finder->name('*.yaml')->files()->in($dirs);
$configs = array();
foreach ( $finder as $file ) {
$configs[] = $parser->parse( $file->getContents() );
}
set_transient('yaml-post-types-configs', $configs);
}
return $configs;
}
public function init() {
try {
$configs = $this->loadConfigs();
...
public function testConfigs() {
delete_transient('yaml-post-types-configs');
add_filter('yamlfy-config-dirs', function( $dirs ) { return array( __DIR__ ); });
$yamlfy = Yamlfy::instance();
$configs = $yamlfy->loadConfigs();
foreach ($configs as $config) {
$this->assertArrayHasKey('post_types',$config);
}
}
}
Awesome
Awesome
Awesome
language: php
php:
- 5.3
- 5.4
- 5.5
env:
- WP_VERSION=latest WP_MULTISITE=0
- WP_VERSION=latest WP_MULTISITE=1
- WP_VERSION=3.8 WP_MULTISITE=0
- WP_VERSION=3.8 WP_MULTISITE=1
before_script:
- bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
script: phpunit
.travis.yml
Enable in travis
Write a new unit test
...
public function testConfigs() {
delete_transient('yaml-post-types-configs');
add_filter('yamlfy-config-dirs', function( $dirs ) { return array( __DIR__ ); });
$yamlfy = Yamlfy::instance();
$configs = $yamlfy->loadConfigs();
foreach ($configs as $config) {
$this->assertArrayHasKey('post_types',$config);
$yamlfy->register_post_types($config['post_types']);
}
// make sure our post type was registered
$post_types = get_post_types();
$this->assertContains('books',$post_types);
}
Git commit and push
$> git add .travis tests/ phpunit.xml bin/test.sh
$> git push origin master
Git commit and push
$> git add .
$> git commit -am "Adding unit tests and travis config"
$> git push origin master
Check travis
Check travis
Check travis
Success