SlideShare a Scribd company logo
1 of 100
Download to read offline
Des Unit Tests
pour vos Plugins
Hein ?
Pourquoi ?
Comment ?
T’es sûr ?
C’est utile ?
C’est fastoche ?
Foreword
Not French ? No sweat !
Just read the code
and
wait for the blog post
(no ETA)
༄༄
Ozh
Ça se prononce « ose »
comme dans « rose »
Mes fils écoutent
de la musique douce
Bonjour ! je m’appelle
Ozh
I ♥ WordPress depuis 11 ans
Premier plugin en août 2004 !!1!!1!§!
des plugins, des articles
http://planetozh.com/blog/
un (super) livre
sur les plugins
http://amzn.to/plugindevbook
mon 1er WordCamp
¯_(ツ)_/¯
et sinon, YOURLS
https://yourls.org/
Inuit
Test
?
(wtf)
Menu
➜ WTF « Unit Test » ?
➜ Installer les outils
➜ Bases de PHPUnit
➜ Cas pratique : plugin + tests
➜ Bonnes pratiques & devoirs à la maison
༄༄
oops #fail …
on est vendredi soir vous dites?
Sans Unit Test
define( 'WP_DEBUG', true );
var_dump( $wp_truc );
die( 'HERE IT WORKS' );
debug_backtrace( );
Chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
rhaaââ mais fuuuuuuu là,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
Ah, ayet, trouvé.
Sans Unit Test :-(
define( 'WP_DEBUG', true );
var_dump( $wp_truc );
die( 'HERE IT WORKS' );
debug_backtrace( );
Chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
rhaaââ mais fuuuuuuu là,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
chercher, modifier, tester,
Ah, ayet, trouvé.
Avec Unit Test
$ 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 4.4.1 by Sebastian Bergmann.
Configuration read from D:homewordpresswp-contentpluginsozh-demophpunit.xml
.....................F..................................... 61 / 124 ( 49%)
........................................................... 119 / 124 ( 95%)
.....
Time: 57.1 seconds, Memory: 66.6Mb
There was 1 failure:
1) This_Feature_Subfeature_Test::test_do_that_thing
Failed asserting that true is false.
D:homewordpresswp-contentpluginsozh-demoteststest-mockfs.php:48
FAILURES!
Tests: 124, Assertions: 1337, Failures: 1.
$ 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 4.4.1 by Sebastian Bergmann.
Configuration read from D:homewordpresswp-contentpluginsozh-demophpunit.xml
.....................F..................................... 61 / 124 ( 49%)
........................................................... 119 / 124 ( 95%)
.....
Time: 57.1 seconds, Memory: 66.6Mb
There was 1 failure:
1) This_Feature_Subfeature_Test::test_do_that_thing
Failed asserting that true is false.
D:homewordpresswp-contentpluginsozh-demoteststest-mockfs.php:48
FAILURES!
Tests: 124, Assertions: 1337, Failures: 1.
Avec Unit Test :-)
Unit Test
Des bouts de code simples
pour vérifier que
chaque partie élementaire
de l’ensemble du code complexe
fonctionne comme attendu
Automatiser !
PHP 5.3 PHP 5.4 PHP 5.5 PHP 5.6 hhvm
WP 4.0 WP 4.1 trunk WP 3.9
multisite = 1 mulsite = 0
Automatiser !
PHP 5.3 PHP 5.4 PHP 5.5 PHP 5.6 hhvm
WP 4.0 WP 4.1 trunk WP 3.9
multisite = 1 mulsite = 0
Easy Digital Download :
21 configurations
350 tests avec 1100 “assertions”
= 23 000 vérifications simples
à chaque commit ou pull request
github.com ➜ travis-ci.org
github.com ➜ travis-ci.org
Elle est bonne ta Pull Request ?
With unit tests and continuous testing,
there is always an extra pair of eyes
looking at your code, helping you find
the bugs you didn't know existed.
Find one bug with unit tests before a
release is pushed out and you will
never NOT have unit test again.
- Pippin Williamson,
Easy Digital Downloads & autres
Bon, on
code ?
Conventions & Pictogrammes
my_file.php
my_file.php (résumé)
$ bash run_script.sh
function do_stuff( )
class Test_Do_Stuff{ }
Bla bla zoom
Ingrédients
• Une CLI confortable
Minimum vital: bash, wget, curl, mysqladmin
• SVN
• GIT
Noob? ➜ « Efficient Git setup on Windows » http://ozh.in/vl
• PHPUnit
• WP-CLI
$ wget https://phar.phpunit.de/phpunit.phar
$ chmod +x phpunit.phar (si besoin)
$ mv phpunit.phar /usr/local/bin/phpunit
ou, dans .bash_profile :
alias phpunit="php d:/php/phpunit.phar
$ phpunit --version
PHPUnit 4.4.1 by Sebastian Bergmann
Installer PHPUnit
Même principe
https://raw.githubusercontent.com/wp-
cli/builds/gh-pages/phar/wp-cli.phar
$ wp --version
WP-CLI 0.17.1
Installer WP-CLI
...
# For wp-cli & phpunit tests
export WP_TESTS_DIR=/d/planetozh/wp-unittest-suite/wordpress-tests-lib
export WP_CORE_DIR=/d/planetozh/wp-unittest-suite/wordpress
...
~/.bash_profile
plugins/my-cool-plugin
et my-cool-plugin.php
Créer les fichiers de test
$ wp scaffold plugin-tests ozh-demo
Success: Created test files.
Créer les fichiers de test
$ wp scaffold plugin-tests ozh-demo
Success: Created test files.
Créer les fichiers de test
language: php
php:
- 5.3
- 5.4
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
#!/usr/bin/env bash
install_wp() { # dans $WP_CORE_DIR
wget https://wordpress.org/wordpress_version_XX.tar.gz
wget https://raw.github.com/markoheijnen/wp-mysqli/master/db.php
}
install_test_suite() { # dans $WP_TESTS_DIR
svn https://develop.svn.wordpress.org/trunk/tests/phpunit/includes/
wget https://develop.svn.wordpress.org/trunk/wp-tests-config-sample.php
}
install_db() {
mysqladmin create $DB_NAME
}
install-wp-tests.sh
$ bash bin/install-wp-tests.sh wordpress_test root '' localhost latest
Installer la « WP Test Suite »
$ bash bin/install-wp-tests.sh wordpress_test root '' localhost latest
+ install_wp
+ mkdir -p C:/Users/Ozh/AppData/Local/Temp/wordpress
+ '[' latest == latest ']'
+ local ARCHIVE_NAME=latest
+ wget -nv -O /tmp/wordpress.tar.gz https://wordpress.org/latest.tar.gz
+ /bin/wget --no-check-certificate -nv -O /tmp/wordpress.tar.gz
https://wordpress.org/latest.tar.gz
WARNING: cannot verify wordpress.org's certificate, issued by
`/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com,
Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate
Authority - G2':
Unable to locally verify the issuer's authority.
WARNING: certificate common name `*.wordpress.org' doesn't match requested host
name `wordpress.org'.
2015-01-02 21:46:40 URL:https://wordpress.org/latest.tar.gz [6183711/6183711] ->
C:/Users/Ozh/AppData/Local/Temp/wordpress .....
...
et patati et patata pendant 3 minutes
Installer la « WP Test Suite »
$ bash bin/install-wp-tests.sh wordpress_test root '' localhost latest
+ install_wp
+ mkdir -p C:/Users/Ozh/AppData/Local/Temp/wordpress
+ '[' latest == latest ']'
+ local ARCHIVE_NAME=latest
+ wget -nv -O /tmp/wordpress.tar.gz https://wordpress.org/latest.tar.gz
+ /bin/wget --no-check-certificate -nv -O /tmp/wordpress.tar.gz
https://wordpress.org/latest.tar.gz
WARNING: cannot verify wordpress.org's certificate, issued by
`/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com,
Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate
Authority - G2':
Unable to locally verify the issuer's authority.
WARNING: certificate common name `*.wordpress.org' doesn't match requested host
name `wordpress.org'.
2015-01-02 21:46:40 URL:https://joomla.org/latest.tar.gz [6183711/6183711] ->
C:/Users/Ozh/CrapData/Local/Temp/weardress .....
...
WP_CORE_DIR
WP_TESTS_DIR
Installer la « WP Test Suite »
<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
1) phpunit.xml
<?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__ ) . '/../ozh-demo.php';
}
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
require $_tests_dir . '/includes/bootstrap.php';
tests/bootstrap.php
1) phpunit.xml ➜ 2) bootstrap.php
<?php
class SampleTest extends WP_UnitTestCase {
function testSample() {
// replace this with some actual testing code
$this->assertTrue( true );
}
}
tests/test-sample.php
1) phpunit.xml ➜ 2) bootstrap.php ➜ 3) test-*.php
$ phpunit
Test micro ? 1, 2 …. 1, 2…
$ 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 4.4.1 by Sebastian Bergmann.
Configuration read from D:homeozhwp-ozh-pluginswp-contentpluginsozh-
demophpunit.xml
.
Time: 6.1 seconds, Memory: 16.75Mb
OK (1 test, 1 assertion)
Test micro ? 1, 2 …. 1, 2…
$ 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 4.4.1 by Sebastian Bergmann.
Configuration read from D:homeozhwp-ozh-pluginswp-contentpluginsozh-
demophpunit.xml
.
Time: 6.1 seconds, Memory: 16.75Mb
OK (1 test, 1 assertion)
Test micro ? 1, 2 …. 1, 2…
Anatomie
d’un
Unit Test
<?php
class Particular_Feature_Test extends WP_UnitTestCase {
function test_sub_feature() {
}
}
tests/test-that-feature.php
<?php
class Particular_Feature_Test extends WP_UnitTestCase {
function test_sub_feature() {
// REUNIR LES INGREDIENTS : contexte et dépendances
// CUISINER : déclencher les actions qu'on veut tester
// GOUTER : vérifier que c'est aussi bon que prévu
}
}
tests/test-that-feature.php
<?php
class Particular_Feature_Test extends WP_UnitTestCase {
function test_sub_feature() {
// REUNIR LES INGREDIENTS : contexte et dépendances
$needed_var = 'something';
$class_to_test = new Class_To_Test();
// CUISINER : déclencher les actions qu'on veut tester
// GOUTER : vérifier que c'est aussi bon que prévu
}
}
tests/test-that-feature.php
<?php
class Particular_Feature_Test extends WP_UnitTestCase {
function test_sub_feature() {
// REUNIR LES INGREDIENTS : contexte et dépendances
$needed_var = 'something';
$class_to_test = new Class_To_Test();
// CUISINER : déclencher les actions qu'on veut tester
$stuff = $class_to_test->feature( $needed_var );
$thing = do_something_with( $stuff );
// GOUTER : vérifier que c'est aussi bon que prévu
}
}
tests/test-that-feature.php
<?php
class Particular_Feature_Test extends WP_UnitTestCase {
function test_sub_feature() {
// REUNIR LES INGREDIENTS : contexte et dépendances
$needed_var = 'something';
$class_to_test = new Class_To_Test();
// CUISINER : déclencher les actions qu'on veut tester
$stuff = $class_to_test->feature( $needed_var );
$thing = do_something_with( $stuff );
// GOUTER : vérifier que c'est aussi bon que prévu
$this->assertEquals( 'résultat attendu', $thing );
$this->assertEmpty( $class_to_test->some_var );
$this->assertArrayHasKey( $stuff );
}
}
tests/test-that-feature.php
PHPUnit : assertions
assertClassHasAttribute()
assertClassHasStaticAttribute()
assertContains()
assertContainsOnly()
assertContainsOnlyInstancesOf()
assertCount()
assertEmpty()
assertEqualXMLStructure()
assertEquals()
assertFalse()
assertFileEquals()
assertFileExists()
assertGreaterThan()
assertGreaterThanOrEqual()
assertInstanceOf()
assertInternalType()
assertJsonFileEqualsJsonFile()
assertJsonStringEqualsJsonFile()
assertJsonStringEqualsJsonString()
assertLessThan()
assertLessThanOrEqual()
assertNull()
assertObjectHasAttribute()
assertRegExp()
assertStringMatchesFormat()
assertStringMatchesFormatFile()
assertSame()
assertStringEndsWith()
assertStringEqualsFile()
assertStringStartsWith()
assertThat()
assertTrue()
assertXmlFileEqualsXmlFile()
assertXmlStringEqualsXmlFile()
assertXmlStringEqualsXmlString()
Principe : assertQuelqueChose() et assertNotQuelqueChose()
Exemple : assertEmpty() et assertNotEmpty()
https://phpunit.de/manual/current/en/appendixes.assertions.html
PHPUnit : setUp( ) & tearDown( )
class Some_Test extends PHPUnit_Framework_TestCase {
function test_quelquechose() {
...
}
function test_autre_chose() {
...
}
}
class Some_Test extends PHPUnit_Framework_TestCase {
function setUp() {
do_things();
}
function tearDown() {
undo_the_things();
}
function test_quelquechose() {
...
}
function test_autre_chose() {
...
}
}
PHPUnit : setUp( ) & tearDown( )
class Some_Test extends PHPUnit_Framework_TestCase {
public static function setUpBeforeClass() {
connect_to_DB();
}
function setUp() {
do_things();
}
function tearDown() {
undo_the_things();
}
function test_quelquechose() {
...
}
function test_autre_chose() {
...
}
}
PHPUnit : setUp( ) & tearDown( )
PHPUnit : debug tip
// bootstrap.php :
function ut_var_dump( $what ) {
ob_start();
var_dump( $what );
$display = ob_get_contents();
ob_end_clean();
fwrite( STDERR, $display );
}
// test :
class My_Cool_Test extends WP_UnitTestCase {
function test_something() {
...
ut_var_dump( $wp_something );
...
}
}
OK… Et
pour les
plugins ?
Le cobaye !
https://github.com/ozh/plugin-unit-test-demo
Test me.
<?php
/*
Plugin Name: Unit Testing Demo Plugin
*/
class Ozh_Demo_Plugin {
function __construct() { }
⤷ function init_plugin() { }
⤷ function add_short_code( $atts ) { }
⤷ function add_meta_if_title( $post_id ) { }
⤷ function get_song_title(){ }
⤷ function update_meta() { }
function activate() { }
⤷ function create_page() { }
⤷ function create_tables() { }
}
new Ozh_Demo_Plugin;
register_activation_hook( __FILE__,
array( 'Ozh_Demo_Plugin', 'activate' ) );
ozh-demo.php
<?php
/*
Plugin Name: Unit Testing Demo Plugin
Plugin URI: https://github.com/ozh/plugin-unit-test-demo
Description: Demo plugin. For unit tests and the lulz
Author: Ozh
Version: 0.1
Author URI: http://ozh.org
*/
class Ozh_Demo_Plugin {
public $post_id;
public $song_title;
/**
* Class contructor
*/
function __construct() {
add_action( 'init', array( $this, 'init_plugin' ) );
do_action( 'ozh_demo_plugin_loaded' );
}
function init_plugin() {
}
__construct( )
/**
* Test the constructor function
*/
class Ozh_Demo_Construct_Test extends WP_UnitTestCase {
public $demo_plugin;
function setUp() {
parent::setUp();
$this->demo_plugin = new Ozh_Demo_Plugin;
}
function test_construct() {
$this->assertEquals(
10,
has_action( 'init', array( $this->demo_plugin, 'init_plugin' ) )
);
$this->assertGreaterThan( 0, did_action( 'ozh_demo_plugin_loaded' ) );
}
}
Ozh_Demo_Construct_Test{ }
/**
* Test the constructor function
*/
class Ozh_Demo_Construct_Test extends WP_UnitTestCase {
public $demo_plugin;
function setUp() {
parent::setUp();
$this->demo_plugin = new Ozh_Demo_Plugin;
}
function test_construct() {
$this->assertEquals(
10,
has_action( 'init', array( $this->demo_plugin, 'init_plugin' ) )
);
$this->assertGreaterThan( 0, did_action( 'ozh_demo_plugin_loaded' ) );
}
}
Ozh_Demo_Construct_Test{ }
<?php
class Ozh_Demo_Plugin {
...
/**
* Plugin init: starts all that's needed
*/
function init_plugin() {
add_action( 'save_post', array( $this, 'add_meta_if_title' ) );
add_shortcode( 'omglol', array( $this, 'add_short_code' ) );
}
function add_meta_if_title( $post_id ) {
// do stuff
}
function add_short_code( $atts ) {
return "OMG c’est trop LOL !";
}
init_plugin( )
<?php
/**
* Test the init function
*/
class Ozh_Demo_Init_Test extends WP_UnitTestCase {
public $demo_plugin;
function setUp() {
}
function test_init_plugin() {
// Simulate WordPress init
do_action( 'init' );
$this->assertEquals(10, has_action('save_post',
array($this->demo_plugin, 'add_meta_if_title' ) ) );
global $shortcode_tags;
$this->assertArrayHasKey( 'omglol', $shortcode_tags );
}
}
Ozh_Demo_Init_Test{ }
<?php
/**
* Test the init function
*/
class Ozh_Demo_Init_Test extends WP_UnitTestCase {
public $demo_plugin;
function setUp() {
}
function test_init_plugin() {
// Simulate WordPress init
do_action( 'init' );
$this->assertEquals(10, has_action('save_post',
array($this->demo_plugin, 'add_meta_if_title' ) ) );
global $shortcode_tags;
$this->assertArrayHasKey( 'omglol', $shortcode_tags );
}
}
Ozh_Demo_Init_Test{ }
Dans vos tests de plugin,
ne testez pas les API WordPress !
Testez la manifestation la plus simple
de la bonne utilisation de l’API.
<?php
class Ozh_Demo_Plugin {
...
/**
* Things to do when the plugin is activated
*/
function activate() {
$this->create_tables();
$this->create_page();
}
function create_tables() {...}
function create_page() {...}
}
new Ozh_Demo_Plugin;
register_activation_hook( __FILE__, array( 'Ozh_Demo_Plugin', 'activate' ) );
activate( )
<?php
/**
* Test the activation function
*/
class Ozh_Demo_Activate_Test extends WP_UnitTestCase {
function test_activate() {
$demo_plugin = $this->getMockBuilder( 'Ozh_Demo_Plugin' )
->setMethods( array( 'create_tables', 'create_page' ) )
->getMock();
$demo_plugin->expects( $this->once() )
->method( 'create_tables' );
$demo_plugin->expects( $this->once() )
->method( 'create_page' );
$demo_plugin->activate();
}
}
Ozh_Demo_Activate_Test{ }
<?php
/**
* Test the activation function
*/
class Ozh_Demo_Activate_Test extends WP_UnitTestCase {
function test_activate() {
$demo_plugin = $this->getMockBuilder( 'Ozh_Demo_Plugin' )
->setMethods( array( 'create_tables', 'create_page' ) )
->getMock();
$demo_plugin->expects( $this->once() )
->method( 'create_tables' );
$demo_plugin->expects( $this->once() )
->method( 'create_page' );
$demo_plugin->activate();
}
}
Ozh_Demo_Activate_Test{ }
Les Mocks
my_feature()
dépendances
Production
Les Mocks
my_feature()
dépendances
Production
my_feature()
mocks
Tests
Mock:
objets simulés qui reproduisent
le comportement d'objets réels
de manière contrôlée
<?php
if (class_exists( 'WP_Image_Editor' ) ) :
class WP_Image_Editor_Mock extends WP_Image_Editor {
...
public function resize( $max_w, $max_h, $crop = false ) {
}
public function multi_resize( $sizes ) {
}
...
}
endif;
WP Mocks : Mock Image Editor
<?php
class Plugin_With_Mail_Test extends WP_UnitTestCase {
function setUp(){
parent::setUp();
$_SERVER['SERVER_NAME'] = 'example.com'; // IMPORTANT
}
function test_something_that_sends_mail() {
...
$sent = wp_mail( 'ozh@ozh.org', 'OMG', 'WTF & KTHXBYE' );
$this->assertTrue( $sent );
...
}
}
WP Mocks : Mock Mailer
WP_FileSystem :
$wp_filesystem->mkdir( $plugin_path . '/config/' );
$wp_filesystem->put_contents( $filename, $content, FS_CHMOD_FILE);
WordPress classes :
➜ WP_Filesystem_Direct
➜ WP_Filesystem_FTPext
➜ WP_Filesystem_ftpsocket
➜ WP_Filesystem_SSH2
http://codex.wordpress.org/Filesystem_API
WordPress Tests classes :
➜ WP_Filesystem_MockFS
https://develop.svn.wordpress.org/trunk/tests/phpunit/tests/filesystem/base.php
WP Mocks : Mock FS
class Ozh_Demo_Plugin {
...
/**
* Create a custom table
*/
function create_tables() {
global $wpdb;
$table_name = $wpdb->prefix . "ozh_demo";
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
`id` mediumint(9) NOT NULL AUTO_INCREMENT,
`time` datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
`name` tinytext NOT NULL,
UNIQUE KEY id (id)
) $charset_collate;";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
return dbDelta( $sql );
}
...
}
create_tables( )
/**
* Test the DB function
*/
class Ozh_Demo_DB_Test extends WP_UnitTestCase {
function test_create_DB() {
global $wpdb;
$demo_plugin = new Ozh_Demo_Plugin;
$created = $demo_plugin->create_tables();
// ut_var_dump( current( $created ) );
// string(30) "Created table wptests_ozh_demo"
$this->assertStringStartsWith( "Created table", current( $created ) );
}
}
Ozh_Demo_Table_Test{ }
class Ozh_Demo_Plugin {
...
/**
* Create a custom page
*/
function create_page() {
global $user_ID;
$page = array(
'post_type' => 'page',
'post_title' => 'Code is Poterie',
'post_content' => 'Opération Template du Dessert',
'post_status' => 'publish',
'post_author' => $user_ID,
);
return wp_insert_post( $page );
}
...
}
create_page( )
<?php
/**
* Test page creation
*/
class Ozh_Demo_Create_Page_Test extends WP_UnitTestCase {
public $demo_plugin;
public static function setUpBeforeClass() {
global $wp_rewrite;
$wp_rewrite->init();
$wp_rewrite->set_permalink_structure( '/%year%/%postid%/' );
$wp_rewrite->flush_rules();
}
public static function tearDownAfterClass() {
global $wp_rewrite;
$wp_rewrite->init();
}
function setUp(){
parent:setUp();
$this->demo_plugin = new Ozh_Demo_Plugin;
}
...
}
Ozh_Demo_Page_Create_Test{ }
<?php
/**
* Test page creation
*/
class Ozh_Demo_Create_Page_Test extends WP_UnitTestCase {
...
function test_create_page_one_way() {
$this->go_to( '/code-is-poterie/' );
$this->assertQueryTrue( 'is_404' );
$this->demo_plugin->create_page();
$this->go_to( '/code-is-poterie/' );
$this->assertQueryTrue( 'is_page', 'is_singular' );
}
}
Ozh_Demo_Page_Create_Test{ }
class Ozh_Demo_Plugin {
...
/**
* Update post meta data 'with_song' with song title
*/
function update_meta() {
update_post_meta( $this->post_id, 'with_song', $this->song_title );
}
...
}
update_meta( )
<?php
/**
* Test the update meta function
*/
class Ozh_Demo_Update_Meta_Test extends WP_UnitTestCase {
public $demo_plugin;
function setUp() { }
function test_update_meta() {
$this->demo_plugin->post_id = ???;
$this->demo_plugin->song_title = ???;
$this->assertEmpty( get_post_meta( $post_id, 'with_song' ) );
$this->demo_plugin->update_meta();
$this->assertNotEmpty( get_post_meta( $post_id, 'with_song' ) );
}
}
Ozh_Demo_Update_Meta_Test{ }
<?php
/**
* Test the update meta function
*/
class Ozh_Demo_Update_Meta_Test extends WP_UnitTestCase {
public $demo_plugin;
function setUp() { }
function test_update_meta() {
$this->demo_plugin->post_id = $this->factory->post->create();
$this->demo_plugin->song_title = rand_str( 32 );
$this->assertEmpty( get_post_meta( $post_id, 'with_song' ) );
$this->demo_plugin->update_meta();
$this->assertNotEmpty( get_post_meta( $post_id, 'with_song' ) );
}
}
Ozh_Demo_Update_Meta_Test{ }
<?php
// Create posts
$page_id = $this->factory->post->create( array( 'post_title' => 'Test Page') );
$this->factory->post->create_many(5, array('post_date' => '2015-01-24 09:45:00'));
// Create users
$user = $this->factory->user->create();
// Comments, Terms, Categories, Tags, Attachments, Blogs, Networks
$this->factory->comment->create();
$this->factory->term->create();
$this->factory->category>create()
$this->factory->attachment->create_object('image.jpg', $post_id, $args);
$this->factory->blog->create();
$this->factory->network->create();
// https://core.trac.wordpress.org/browser/trunk/tests/phpunit/includes/factory.php
L’objet factory
<?php
/**
* Test the update meta function
*/
class Ozh_Demo_Update_Meta_Test extends WP_UnitTestCase {
public $demo_plugin;
function setUp() { }
function test_update_meta() {
$this->demo_plugin->post_id = $this->factory->post->create();
$this->demo_plugin->song_title = rand_str( 32 );
$this->assertEmpty( get_post_meta( $post_id, 'with_song' ) );
$this->demo_plugin->update_meta();
$this->assertNotEmpty( get_post_meta( $post_id, 'with_song' ) );
}
}
Ozh_Demo_Update_Meta_Test{ }
class Ozh_Demo_Plugin {
...
/**
* Get current song title played on that cool web radio
*/
function get_song_title(){
$remote = 'http://www.radiometal.com/player/song_infos_player.txt';
$body = wp_remote_retrieve_body( wp_remote_get( $remote ) );
/* sample return:
<div id='playerartistname'>Metallica</div>
<div id='playersongname'>Master of Puppets</div>
*/
preg_match_all( "!<div id='[^']*'>([^<]*)</div>!", $body, $matches );
// $matches[1][0]: artist - $matches[1][1]: song name
if( $matches[1] ) {
return $matches[1][0] . ' - ' . $matches[1][1];
} else {
return false;
}
}
}
get_song_title( )
<?php
/**
* Test the remote request functions
*/
class Ozh_Demo_Remote_Test extends WP_UnitTestCase {
public $demo_plugin; function setUp(){ }
function return_valid_html() {
$html = "BLAH <div id='playerartistname'>Slayer</div>
<div id='playersongname'>Raining Blood</div> BLAH";
return array( 'body' => $html );
}
function test_remote_request_valid_HTML() {
add_filter( 'pre_http_request', array( $this, 'return_valid_html' ) );
$this->assertSame(
'Slayer - Raining Blood',
$this->demo_plugin->get_song_title() );
}
}
Ozh_Demo_Remote_Test{ }
<?php
/**
* Test the remote request functions
*/
class Ozh_Demo_Remote_Test extends WP_UnitTestCase {
function return_crap_html() {
return array( 'body' => "Error 500 - come back later" );
}
function return_no_html() {
return array( );
}
function test_remote_request_invalid_HTML() {
add_filter( 'pre_http_request', array( $this, 'return_crap_html' ) );
$this->assertFalse( $this->demo_plugin->get_song_title() );
add_filter( 'pre_http_request', array( $this, 'return_no_html' ) );
$this->assertFalse( $this->demo_plugin->get_song_title() );
}
}
Ozh_Demo_Remote_Test{ }
<?php
/**
* Test the remote request functions
*/
class Ozh_Demo_Remote_Test extends WP_UnitTestCase {
function return_crap_html() {
return array( 'body' => "Error 500 - come back later" );
}
function return_no_html() {
return array( );
}
function return_valid_html() {
}
Ozh_Demo_Remote_Test{ }
Trucs divers
à retenir ou à faire
function do_something( )
➜ Janvier 2015 :
2 variables
+ 1 test
= 1 valeur attendue
➜ Janvier 2018 :
6 variables
+ 3 tests
+ 5 fonctions internes
+ 2 librairies externes
= la même valeur attendue !
Testez aussi le trivial !
<?php
require_once '/path/to/your-theme/functions.php';
function _manually_load_theme() {
switch_theme( 'your-theme' );
}
tests_add_filter( 'muplugins_loaded', '_manually_load_theme' );
Testez vos fonctions de thèmes !
Testable ou dé-testable ?
class Ozh_Demo_Plugin {
function add_meta_if_title( ) {
if ( 'DOING_AUTOSAVE' ) ...
if ( ! current_user_can( ) ) ...
$this->get_song_title();
if( $this->song_title ) {
$this->update_meta();
}
}
function update_meta() { ... }
function get_song_title(){ ... }
}
class Ozh_Demo_Plugin {
function add_meta_if_title() {
if ( 'DOING_AUTOSAVE' ) ...
if ( ! current_user_can( ) ) ...
$remote = ...;
$body = wp_remote_retrieve_body( );
preg_match_all( ... ) ;
$this->song_title = ...;
if( $this->song_title ) {
update_post_meta( ... );
}
}
}
Testable ou dé-testable ?
class Ozh_Demo_Plugin {
function add_meta_if_title( ) {
if ( 'DOING_AUTOSAVE' ) ...
if ( ! current_user_can( ) ) ...
$this->get_song_title();
if( $this->song_title ) {
$this->update_meta();
}
}
function update_meta() { ... }
function get_song_title(){ ... }
}
class Ozh_Demo_Plugin {
function add_meta_if_title() {
if ( 'DOING_AUTOSAVE' ) ...
if ( ! current_user_can( ) ) ...
$remote = ...;
$body = wp_remote_retrieve_body( );
preg_match_all( ... ) ;
$this->song_title = ...;
if( $this->song_title ) {
update_post_meta( ... );
}
}
}
Testable = atomique
class Ozh_Demo_Plugin {
function add_meta_if_title( ) {
if ( 'DOING_AUTOSAVE' ) ...
if ( ! current_user_can( ) ) ...
$this->get_song_title();
if( $this->song_title ) {
$this->update_meta();
}
}
function update_meta() { ... }
function get_song_title(){ ... }
}
# Dans .gitattributes
# Exclude certain files or directories when generating an archive
.travis.yml export-ignore
composer.* export-ignore
.git* export-ignore
bin export-ignore
tests export-ignore
phpunit.xml export-ignore
Pas de tests dans vos archive.zip ?
# Dans .gitattributes
# Exclude certain files or directories when generating an archive
.travis.yml export-ignore
composer.* export-ignore
.git* export-ignore
bin export-ignore
tests export-ignore
phpunit.xml export-ignore
Pas de tests dans vos archive.zip ?
1) Bootstrap.php :
require_once dirname( __FILE__ ) . '/My_Custom_Assertions.php';
Etendez l’objet WP_UnitTestCase
1) Bootstrap.php :
require_once dirname( __FILE__ ) . '/My_Custom_Assertions.php';
2) My_Custom_Assertions.php :
class MyPlugin_UnitTestCase extends WP_UnitTestCase {
/**
* Assert something specific to my plugin
*/
function assertSpecificThing(...) {
...
$this->assertFalse(...)
}
}
Etendez l’objet WP_UnitTestCase
1) Bootstrap.php :
require_once dirname( __FILE__ ) . '/My_Custom_Assertions.php';
2) My_Custom_Assertions.php :
class MyPlugin_UnitTestCase extends WP_UnitTestCase {
/**
* Assert something specific to my plugin
*/
function assertSpecificThing(...) {
...
$this->assertFalse(...)
}
}
3) tests/test-something.php :
class Something_Test extends MyPlugin_UnitTestCase {
...
}
Etendez l’objet WP_UnitTestCase
1) Bootstrap.php :
require_once dirname( __FILE__ ) . '/My_Custom_Assertions.php';
2) My_Custom_Assertions.php :
class MyPlugin_UnitTestCase extends WP_UnitTestCase {
/**
* Assert something specific to my plugin
*/
function assertSpecificThing(...) {
...
$this->assertFalse(...)
}
}
3) tests/test-something.php :
class Something_Test extends MyPlugin_UnitTestCase {
...
}
Exemples :
assertArchiveEquals(), assertArchiveContains(), assertArchiveFileCount()
https://github.com/humanmade/backupwordpress/blob/master/tests/class-wp-test-hm-backup-testcase.php
Etendez l’objet WP_UnitTestCase
phpunit-coverage.xml
<phpunit
bootstrap="tests/bootstrap.php"
...
>
<filter>
<blacklist>
<directory>/home/planetozh/wp-unittest-suite</directory>
<directory>./tests</directory>
</blacklist>
</filter>
<logging>
<log type="coverage-html" target="coverage" title="PHPUnit"
charset="UTF-8" yui="true" highlight="true"
lowUpperBound="50" highLowerBound="90"/>
</logging>
</phpunit>
Générer le rapport de code coverage
$ phpunit -c phpunit-coverage.xml
Vérifiez votre Code Coverage
phpunit-coverage.xml
<phpunit
bootstrap="tests/bootstrap.php"
...
>
<filter>
<blacklist>
<directory>/home/planetozh/wp-unittest-suite</directory>
<directory>./tests</directory>
</blacklist>
</filter>
<logging>
<log type="coverage-html" target="coverage" title="PHPUnit"
charset="UTF-8" yui="true" highlight="true"
lowUpperBound="50" highLowerBound="90"/>
</logging>
</phpunit>
Générer le rapport de code coverage
$ phpunit -c phpunit-coverage.xml
Vérifiez votre Code Coverage
Selenium - http://www.seleniumhq.org/
<?php
// Selenium - automates browsers.
class ConnectTest extends PHPUnit_Extensions_Selenium2TestCase {
protected function setUp() {
$this->setBrowser("*chrome");
$this->setBrowserUrl("https://mylocal.dev/");
}
public function testUserCanLogIn() {
$this->open("/");
$this->click("css=img[alt="Twitter"]");
$this->waitForPageToLoad("30000");
$this->assertContains( ‘dashboard’, $this->title() );
}
}
Qunit – http://qunitjs.com/
<script>
test("Array should contain four items", function () {
equal( myArray.length, 4, "Pass! - array contains four items");
});
</script>
Testez vos interfaces !
Selenium - http://www.seleniumhq.org/
<?php
// Selenium - automates browsers.
class ConnectTest extends PHPUnit_Extensions_Selenium2TestCase {
protected function setUp() {
$this->setBrowser("*chrome");
$this->setBrowserUrl("https://mylocal.dev/");
}
public function testUserCanLogIn() {
$this->open("/");
$this->click("css=img[alt="Twitter"]");
$this->waitForPageToLoad("30000");
$this->assertContains( ‘dashboard’, $this->title() );
}
}
Qunit – http://qunitjs.com/
<script>
test("Array should contain four items", function () {
equal( myArray.length, 4, "Pass! - array contains four items");
});
</script>
Testez vos interfaces !
L’excellente documentation de PHPUnit
➜ https://phpunit.de/documentation.html
Des tutoriaux supplémentaires
➜ http://voceplatforms.com/?s=unit+testing+wordpress
➜ https://pippinsplugins.com/tag/unit-tests/
➜ http://planetozh.com/blog/ ??
Les Unit Tests de WordPress
➜ http://develop.svn.wordpress.org/trunk/
Des plugins avec tests
➜ https://github.com/easydigitaldownloads/Easy-Digital-Downloads
➜ https://github.com/humanmade/backupwordpress
Et coder des tests !
➜ Ecrire 10 tests la semaine prochaine
➜ Ecrire 100 tests le mois suivant ?
➜ 1 bug corrigé = 1 nouveau test
Devoirs à la maison
?
Merci !
➜ http://ozh.in/feedback
Questions ?

More Related Content

What's hot

Py conkr 20150829_docker-python
Py conkr 20150829_docker-pythonPy conkr 20150829_docker-python
Py conkr 20150829_docker-pythonEric Ahn
 
DiUS Computing Lca Rails Final
DiUS  Computing Lca Rails FinalDiUS  Computing Lca Rails Final
DiUS Computing Lca Rails FinalRobert Postill
 
Using Puppet to Create a Dynamic Network - PuppetConf 2013
Using Puppet to Create a Dynamic Network - PuppetConf 2013Using Puppet to Create a Dynamic Network - PuppetConf 2013
Using Puppet to Create a Dynamic Network - PuppetConf 2013Puppet
 
How to deploy node to production
How to deploy node to productionHow to deploy node to production
How to deploy node to productionSean Hess
 
The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...
The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...
The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...Puppet
 
PyParis 2017 / Writing a C Python extension in 2017, Jean-Baptiste Aviat
PyParis 2017 / Writing a C Python extension in 2017, Jean-Baptiste Aviat PyParis 2017 / Writing a C Python extension in 2017, Jean-Baptiste Aviat
PyParis 2017 / Writing a C Python extension in 2017, Jean-Baptiste Aviat Pôle Systematic Paris-Region
 
Adventures in infrastructure as code
Adventures in infrastructure as codeAdventures in infrastructure as code
Adventures in infrastructure as codeJulian Simpson
 
Concurrency in Python
Concurrency in PythonConcurrency in Python
Concurrency in PythonMosky Liu
 
Infrastructure as code might be literally impossible
Infrastructure as code might be literally impossibleInfrastructure as code might be literally impossible
Infrastructure as code might be literally impossibleice799
 
Web backends development using Python
Web backends development using PythonWeb backends development using Python
Web backends development using PythonAyun Park
 
Caching and tuning fun for high scalability @ FrOSCon 2011
Caching and tuning fun for high scalability @ FrOSCon 2011Caching and tuning fun for high scalability @ FrOSCon 2011
Caching and tuning fun for high scalability @ FrOSCon 2011Wim Godden
 
Node.js basics
Node.js basicsNode.js basics
Node.js basicsBen Lin
 
Node.js API 서버 성능 개선기
Node.js API 서버 성능 개선기Node.js API 서버 성능 개선기
Node.js API 서버 성능 개선기JeongHun Byeon
 
Node.js Event Loop & EventEmitter
Node.js Event Loop & EventEmitterNode.js Event Loop & EventEmitter
Node.js Event Loop & EventEmitterSimen Li
 
Introduction to puppet - Hands on Session at HPI Potsdam
Introduction to puppet - Hands on Session at HPI PotsdamIntroduction to puppet - Hands on Session at HPI Potsdam
Introduction to puppet - Hands on Session at HPI PotsdamChristoph Oelmüller
 
Intro to pl/PHP Oscon2007
Intro to pl/PHP Oscon2007Intro to pl/PHP Oscon2007
Intro to pl/PHP Oscon2007Robert Treat
 
Solaris Kernel Debugging V1.0
Solaris Kernel Debugging V1.0Solaris Kernel Debugging V1.0
Solaris Kernel Debugging V1.0Jarod Wang
 

What's hot (20)

Py conkr 20150829_docker-python
Py conkr 20150829_docker-pythonPy conkr 20150829_docker-python
Py conkr 20150829_docker-python
 
DiUS Computing Lca Rails Final
DiUS  Computing Lca Rails FinalDiUS  Computing Lca Rails Final
DiUS Computing Lca Rails Final
 
Using Puppet to Create a Dynamic Network - PuppetConf 2013
Using Puppet to Create a Dynamic Network - PuppetConf 2013Using Puppet to Create a Dynamic Network - PuppetConf 2013
Using Puppet to Create a Dynamic Network - PuppetConf 2013
 
How to deploy node to production
How to deploy node to productionHow to deploy node to production
How to deploy node to production
 
The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...
The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...
The Puppet Debugging Kit: Building Blocks for Exploration and Problem Solving...
 
PyParis 2017 / Writing a C Python extension in 2017, Jean-Baptiste Aviat
PyParis 2017 / Writing a C Python extension in 2017, Jean-Baptiste Aviat PyParis 2017 / Writing a C Python extension in 2017, Jean-Baptiste Aviat
PyParis 2017 / Writing a C Python extension in 2017, Jean-Baptiste Aviat
 
Adventures in infrastructure as code
Adventures in infrastructure as codeAdventures in infrastructure as code
Adventures in infrastructure as code
 
Concurrency in Python
Concurrency in PythonConcurrency in Python
Concurrency in Python
 
Troubleshooting Puppet
Troubleshooting PuppetTroubleshooting Puppet
Troubleshooting Puppet
 
Infrastructure as code might be literally impossible
Infrastructure as code might be literally impossibleInfrastructure as code might be literally impossible
Infrastructure as code might be literally impossible
 
Web backends development using Python
Web backends development using PythonWeb backends development using Python
Web backends development using Python
 
Docker, c'est bonheur !
Docker, c'est bonheur !Docker, c'est bonheur !
Docker, c'est bonheur !
 
Caching and tuning fun for high scalability @ FrOSCon 2011
Caching and tuning fun for high scalability @ FrOSCon 2011Caching and tuning fun for high scalability @ FrOSCon 2011
Caching and tuning fun for high scalability @ FrOSCon 2011
 
Node.js basics
Node.js basicsNode.js basics
Node.js basics
 
Node.js API 서버 성능 개선기
Node.js API 서버 성능 개선기Node.js API 서버 성능 개선기
Node.js API 서버 성능 개선기
 
VS Debugging Tricks
VS Debugging TricksVS Debugging Tricks
VS Debugging Tricks
 
Node.js Event Loop & EventEmitter
Node.js Event Loop & EventEmitterNode.js Event Loop & EventEmitter
Node.js Event Loop & EventEmitter
 
Introduction to puppet - Hands on Session at HPI Potsdam
Introduction to puppet - Hands on Session at HPI PotsdamIntroduction to puppet - Hands on Session at HPI Potsdam
Introduction to puppet - Hands on Session at HPI Potsdam
 
Intro to pl/PHP Oscon2007
Intro to pl/PHP Oscon2007Intro to pl/PHP Oscon2007
Intro to pl/PHP Oscon2007
 
Solaris Kernel Debugging V1.0
Solaris Kernel Debugging V1.0Solaris Kernel Debugging V1.0
Solaris Kernel Debugging V1.0
 

Viewers also liked

L’i18n et les outils de l10n en 2015 - WCParis 2015
L’i18n et les outils de l10n en 2015 - WCParis 2015L’i18n et les outils de l10n en 2015 - WCParis 2015
L’i18n et les outils de l10n en 2015 - WCParis 2015Bénard François-Xavier
 
Présentation WordCamp Paris 2015 - Inside da WeB
Présentation WordCamp Paris 2015 - Inside da WeBPrésentation WordCamp Paris 2015 - Inside da WeB
Présentation WordCamp Paris 2015 - Inside da WeBSébastien Grillot
 
Décoller votre site avec Jetpack
Décoller votre site avec JetpackDécoller votre site avec Jetpack
Décoller votre site avec JetpackRichard Archambault
 
Wordcamp paris 2015 dev-pragmatique-bonnes-pratiques
Wordcamp paris 2015  dev-pragmatique-bonnes-pratiquesWordcamp paris 2015  dev-pragmatique-bonnes-pratiques
Wordcamp paris 2015 dev-pragmatique-bonnes-pratiquesSylvie Clément
 
Modernizing WordPress Search with Elasticsearch
Modernizing WordPress Search with ElasticsearchModernizing WordPress Search with Elasticsearch
Modernizing WordPress Search with ElasticsearchTaylor Lovett
 
Développer en javascript une extension de A a Z
Développer en javascript une extension de A a ZDévelopper en javascript une extension de A a Z
Développer en javascript une extension de A a ZNicolas Juen
 
Industrialiser votre maintenance sous WordPress
Industrialiser votre maintenance sous WordPressIndustrialiser votre maintenance sous WordPress
Industrialiser votre maintenance sous WordPressAurélien Denis
 
Organiser un événement WordPress - WordCamp Paris 2015
Organiser un événement WordPress - WordCamp Paris 2015Organiser un événement WordPress - WordCamp Paris 2015
Organiser un événement WordPress - WordCamp Paris 2015Daniel Roch - SeoMix
 
Constructeurs de page WordPress
Constructeurs de page WordPressConstructeurs de page WordPress
Constructeurs de page WordPressFabrice Ducarme
 
Freelance & WordPress (WordCamp Paris 2015)
Freelance & WordPress (WordCamp Paris 2015)Freelance & WordPress (WordCamp Paris 2015)
Freelance & WordPress (WordCamp Paris 2015)Boiteaweb
 
WordPress comme back office d'applications mobiles
WordPress comme back office d'applications mobilesWordPress comme back office d'applications mobiles
WordPress comme back office d'applications mobilesBenjamin LUPU
 
WordCamp Paris 2015 - Marketing WooCommerce pour augmenter les ventes - Rémi ...
WordCamp Paris 2015 - Marketing WooCommerce pour augmenter les ventes - Rémi ...WordCamp Paris 2015 - Marketing WooCommerce pour augmenter les ventes - Rémi ...
WordCamp Paris 2015 - Marketing WooCommerce pour augmenter les ventes - Rémi ...corsonr
 

Viewers also liked (13)

L’i18n et les outils de l10n en 2015 - WCParis 2015
L’i18n et les outils de l10n en 2015 - WCParis 2015L’i18n et les outils de l10n en 2015 - WCParis 2015
L’i18n et les outils de l10n en 2015 - WCParis 2015
 
Présentation WordCamp Paris 2015 - Inside da WeB
Présentation WordCamp Paris 2015 - Inside da WeBPrésentation WordCamp Paris 2015 - Inside da WeB
Présentation WordCamp Paris 2015 - Inside da WeB
 
Décoller votre site avec Jetpack
Décoller votre site avec JetpackDécoller votre site avec Jetpack
Décoller votre site avec Jetpack
 
Wordcamp paris 2015 dev-pragmatique-bonnes-pratiques
Wordcamp paris 2015  dev-pragmatique-bonnes-pratiquesWordcamp paris 2015  dev-pragmatique-bonnes-pratiques
Wordcamp paris 2015 dev-pragmatique-bonnes-pratiques
 
Modernizing WordPress Search with Elasticsearch
Modernizing WordPress Search with ElasticsearchModernizing WordPress Search with Elasticsearch
Modernizing WordPress Search with Elasticsearch
 
Développer en javascript une extension de A a Z
Développer en javascript une extension de A a ZDévelopper en javascript une extension de A a Z
Développer en javascript une extension de A a Z
 
Industrialiser votre maintenance sous WordPress
Industrialiser votre maintenance sous WordPressIndustrialiser votre maintenance sous WordPress
Industrialiser votre maintenance sous WordPress
 
Organiser un événement WordPress - WordCamp Paris 2015
Organiser un événement WordPress - WordCamp Paris 2015Organiser un événement WordPress - WordCamp Paris 2015
Organiser un événement WordPress - WordCamp Paris 2015
 
Constructeurs de page WordPress
Constructeurs de page WordPressConstructeurs de page WordPress
Constructeurs de page WordPress
 
Freelance & WordPress (WordCamp Paris 2015)
Freelance & WordPress (WordCamp Paris 2015)Freelance & WordPress (WordCamp Paris 2015)
Freelance & WordPress (WordCamp Paris 2015)
 
reveal.js 3.0.0
reveal.js 3.0.0reveal.js 3.0.0
reveal.js 3.0.0
 
WordPress comme back office d'applications mobiles
WordPress comme back office d'applications mobilesWordPress comme back office d'applications mobiles
WordPress comme back office d'applications mobiles
 
WordCamp Paris 2015 - Marketing WooCommerce pour augmenter les ventes - Rémi ...
WordCamp Paris 2015 - Marketing WooCommerce pour augmenter les ventes - Rémi ...WordCamp Paris 2015 - Marketing WooCommerce pour augmenter les ventes - Rémi ...
WordCamp Paris 2015 - Marketing WooCommerce pour augmenter les ventes - Rémi ...
 

Similar to WordPress Plugin Unit Tests (FR - WordCamp Paris 2015)

Vagrant WordCamp Hamilton
Vagrant  WordCamp HamiltonVagrant  WordCamp Hamilton
Vagrant WordCamp HamiltonPaul Bearne
 
Getting started with PHPUnit
Getting started with PHPUnitGetting started with PHPUnit
Getting started with PHPUnitKhyati Gala
 
Scripting for infosecs
Scripting for infosecsScripting for infosecs
Scripting for infosecsnancysuemartin
 
The Modern Developer Toolbox
The Modern Developer ToolboxThe Modern Developer Toolbox
The Modern Developer ToolboxPablo Godel
 
DevOps Series: Extending vagrant with Puppet for configuration management
DevOps Series: Extending vagrant with Puppet for configuration managementDevOps Series: Extending vagrant with Puppet for configuration management
DevOps Series: Extending vagrant with Puppet for configuration managementFelipe
 
Dexterity in 15 minutes or less
Dexterity in 15 minutes or lessDexterity in 15 minutes or less
Dexterity in 15 minutes or lessrijk.stofberg
 
A General Purpose Docker Image for PHP
A General Purpose Docker Image for PHPA General Purpose Docker Image for PHP
A General Purpose Docker Image for PHPRobert Lemke
 
Website releases made easy with the PEAR installer, OSCON 2009
Website releases made easy with the PEAR installer, OSCON 2009Website releases made easy with the PEAR installer, OSCON 2009
Website releases made easy with the PEAR installer, OSCON 2009Helgi Þormar Þorbjörnsson
 
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit SoftwaretestsEffizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit SoftwaretestsDECK36
 
Deploying Symfony | symfony.cat
Deploying Symfony | symfony.catDeploying Symfony | symfony.cat
Deploying Symfony | symfony.catPablo Godel
 
The Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/PressThe Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/PressJeroen van Dijk
 
Modern tooling to assist with developing applications on FreeBSD
Modern tooling to assist with developing applications on FreeBSDModern tooling to assist with developing applications on FreeBSD
Modern tooling to assist with developing applications on FreeBSDSean Chittenden
 
Web development automatisation for fun and profit (Artem Daniliants)
Web development automatisation for fun and profit (Artem Daniliants)Web development automatisation for fun and profit (Artem Daniliants)
Web development automatisation for fun and profit (Artem Daniliants)LumoSpark
 
Magento 2 Development
Magento 2 DevelopmentMagento 2 Development
Magento 2 DevelopmentDuke Dao
 
CI workflow in a web studio
CI workflow in a web studioCI workflow in a web studio
CI workflow in a web studiodeWeb
 
Diagnosing WordPress: What to do when things go wrong
Diagnosing WordPress: What to do when things go wrongDiagnosing WordPress: What to do when things go wrong
Diagnosing WordPress: What to do when things go wrongWordCamp Sydney
 
Тарас Кирилюк та Олена Пустовойт — CI workflow у веб-студії
Тарас Кирилюк та Олена Пустовойт — CI workflow у веб-студіїТарас Кирилюк та Олена Пустовойт — CI workflow у веб-студії
Тарас Кирилюк та Олена Пустовойт — CI workflow у веб-студіїLEDC 2016
 
Continous Delivering a PHP application
Continous Delivering a PHP applicationContinous Delivering a PHP application
Continous Delivering a PHP applicationJavier López
 
One commit, one release. Continuously delivering a Symfony project.
One commit, one release. Continuously delivering a Symfony project.One commit, one release. Continuously delivering a Symfony project.
One commit, one release. Continuously delivering a Symfony project.Javier López
 

Similar to WordPress Plugin Unit Tests (FR - WordCamp Paris 2015) (20)

Vagrant WordCamp Hamilton
Vagrant  WordCamp HamiltonVagrant  WordCamp Hamilton
Vagrant WordCamp Hamilton
 
Getting started with PHPUnit
Getting started with PHPUnitGetting started with PHPUnit
Getting started with PHPUnit
 
Scripting for infosecs
Scripting for infosecsScripting for infosecs
Scripting for infosecs
 
The Modern Developer Toolbox
The Modern Developer ToolboxThe Modern Developer Toolbox
The Modern Developer Toolbox
 
DevOps Series: Extending vagrant with Puppet for configuration management
DevOps Series: Extending vagrant with Puppet for configuration managementDevOps Series: Extending vagrant with Puppet for configuration management
DevOps Series: Extending vagrant with Puppet for configuration management
 
Dexterity in 15 minutes or less
Dexterity in 15 minutes or lessDexterity in 15 minutes or less
Dexterity in 15 minutes or less
 
A General Purpose Docker Image for PHP
A General Purpose Docker Image for PHPA General Purpose Docker Image for PHP
A General Purpose Docker Image for PHP
 
Website releases made easy with the PEAR installer, OSCON 2009
Website releases made easy with the PEAR installer, OSCON 2009Website releases made easy with the PEAR installer, OSCON 2009
Website releases made easy with the PEAR installer, OSCON 2009
 
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit SoftwaretestsEffizientere WordPress-Plugin-Entwicklung mit Softwaretests
Effizientere WordPress-Plugin-Entwicklung mit Softwaretests
 
Deploying Symfony | symfony.cat
Deploying Symfony | symfony.catDeploying Symfony | symfony.cat
Deploying Symfony | symfony.cat
 
The Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/PressThe Enterprise Wor/d/thy/Press
The Enterprise Wor/d/thy/Press
 
Modern tooling to assist with developing applications on FreeBSD
Modern tooling to assist with developing applications on FreeBSDModern tooling to assist with developing applications on FreeBSD
Modern tooling to assist with developing applications on FreeBSD
 
Web development automatisation for fun and profit (Artem Daniliants)
Web development automatisation for fun and profit (Artem Daniliants)Web development automatisation for fun and profit (Artem Daniliants)
Web development automatisation for fun and profit (Artem Daniliants)
 
PHP selber bauen
PHP selber bauenPHP selber bauen
PHP selber bauen
 
Magento 2 Development
Magento 2 DevelopmentMagento 2 Development
Magento 2 Development
 
CI workflow in a web studio
CI workflow in a web studioCI workflow in a web studio
CI workflow in a web studio
 
Diagnosing WordPress: What to do when things go wrong
Diagnosing WordPress: What to do when things go wrongDiagnosing WordPress: What to do when things go wrong
Diagnosing WordPress: What to do when things go wrong
 
Тарас Кирилюк та Олена Пустовойт — CI workflow у веб-студії
Тарас Кирилюк та Олена Пустовойт — CI workflow у веб-студіїТарас Кирилюк та Олена Пустовойт — CI workflow у веб-студії
Тарас Кирилюк та Олена Пустовойт — CI workflow у веб-студії
 
Continous Delivering a PHP application
Continous Delivering a PHP applicationContinous Delivering a PHP application
Continous Delivering a PHP application
 
One commit, one release. Continuously delivering a Symfony project.
One commit, one release. Continuously delivering a Symfony project.One commit, one release. Continuously delivering a Symfony project.
One commit, one release. Continuously delivering a Symfony project.
 

Recently uploaded

OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingOpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingShane Coughlan
 
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsSensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsChristian Birchler
 
2024 DevNexus Patterns for Resiliency: Shuffle shards
2024 DevNexus Patterns for Resiliency: Shuffle shards2024 DevNexus Patterns for Resiliency: Shuffle shards
2024 DevNexus Patterns for Resiliency: Shuffle shardsChristopher Curtin
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfMarharyta Nedzelska
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLionel Briand
 
Introduction to Firebase Workshop Slides
Introduction to Firebase Workshop SlidesIntroduction to Firebase Workshop Slides
Introduction to Firebase Workshop Slidesvaideheekore1
 
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingOpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingShane Coughlan
 
eSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolseSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolsosttopstonverter
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecturerahul_net
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfEnhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfRTS corp
 
VictoriaMetrics Q1 Meet Up '24 - Community & News Update
VictoriaMetrics Q1 Meet Up '24 - Community & News UpdateVictoriaMetrics Q1 Meet Up '24 - Community & News Update
VictoriaMetrics Q1 Meet Up '24 - Community & News UpdateVictoriaMetrics
 
Salesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZSalesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZABSYZ Inc
 
Keeping your build tool updated in a multi repository world
Keeping your build tool updated in a multi repository worldKeeping your build tool updated in a multi repository world
Keeping your build tool updated in a multi repository worldRoberto Pérez Alcolea
 
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxThe Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxRTS corp
 
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptxReal-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptxRTS corp
 
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...Bert Jan Schrijver
 
Patterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencePatterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencessuser9e7c64
 
VictoriaMetrics Anomaly Detection Updates: Q1 2024
VictoriaMetrics Anomaly Detection Updates: Q1 2024VictoriaMetrics Anomaly Detection Updates: Q1 2024
VictoriaMetrics Anomaly Detection Updates: Q1 2024VictoriaMetrics
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 

Recently uploaded (20)

OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingOpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
 
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsSensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
 
2024 DevNexus Patterns for Resiliency: Shuffle shards
2024 DevNexus Patterns for Resiliency: Shuffle shards2024 DevNexus Patterns for Resiliency: Shuffle shards
2024 DevNexus Patterns for Resiliency: Shuffle shards
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdf
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and Repair
 
Introduction to Firebase Workshop Slides
Introduction to Firebase Workshop SlidesIntroduction to Firebase Workshop Slides
Introduction to Firebase Workshop Slides
 
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingOpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
 
eSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration toolseSoftTools IMAP Backup Software and migration tools
eSoftTools IMAP Backup Software and migration tools
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecture
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfEnhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
 
VictoriaMetrics Q1 Meet Up '24 - Community & News Update
VictoriaMetrics Q1 Meet Up '24 - Community & News UpdateVictoriaMetrics Q1 Meet Up '24 - Community & News Update
VictoriaMetrics Q1 Meet Up '24 - Community & News Update
 
Salesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZSalesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZ
 
Keeping your build tool updated in a multi repository world
Keeping your build tool updated in a multi repository worldKeeping your build tool updated in a multi repository world
Keeping your build tool updated in a multi repository world
 
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptxThe Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
The Role of IoT and Sensor Technology in Cargo Cloud Solutions.pptx
 
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptxReal-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
Real-time Tracking and Monitoring with Cargo Cloud Solutions.pptx
 
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
 
Patterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencePatterns for automating API delivery. API conference
Patterns for automating API delivery. API conference
 
VictoriaMetrics Anomaly Detection Updates: Q1 2024
VictoriaMetrics Anomaly Detection Updates: Q1 2024VictoriaMetrics Anomaly Detection Updates: Q1 2024
VictoriaMetrics Anomaly Detection Updates: Q1 2024
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 

WordPress Plugin Unit Tests (FR - WordCamp Paris 2015)

  • 1. Des Unit Tests pour vos Plugins Hein ? Pourquoi ? Comment ? T’es sûr ? C’est utile ? C’est fastoche ?
  • 2. Foreword Not French ? No sweat ! Just read the code and wait for the blog post (no ETA) ༄༄
  • 3. Ozh Ça se prononce « ose » comme dans « rose » Mes fils écoutent de la musique douce Bonjour ! je m’appelle
  • 4. Ozh I ♥ WordPress depuis 11 ans Premier plugin en août 2004 !!1!!1!§! des plugins, des articles http://planetozh.com/blog/ un (super) livre sur les plugins http://amzn.to/plugindevbook mon 1er WordCamp ¯_(ツ)_/¯ et sinon, YOURLS https://yourls.org/
  • 5.
  • 7. Menu ➜ WTF « Unit Test » ? ➜ Installer les outils ➜ Bases de PHPUnit ➜ Cas pratique : plugin + tests ➜ Bonnes pratiques & devoirs à la maison ༄༄
  • 8.
  • 9. oops #fail … on est vendredi soir vous dites?
  • 10. Sans Unit Test define( 'WP_DEBUG', true ); var_dump( $wp_truc ); die( 'HERE IT WORKS' ); debug_backtrace( ); Chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, rhaaââ mais fuuuuuuu là, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, Ah, ayet, trouvé.
  • 11. Sans Unit Test :-( define( 'WP_DEBUG', true ); var_dump( $wp_truc ); die( 'HERE IT WORKS' ); debug_backtrace( ); Chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, rhaaââ mais fuuuuuuu là, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, chercher, modifier, tester, Ah, ayet, trouvé.
  • 12. Avec Unit Test $ 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 4.4.1 by Sebastian Bergmann. Configuration read from D:homewordpresswp-contentpluginsozh-demophpunit.xml .....................F..................................... 61 / 124 ( 49%) ........................................................... 119 / 124 ( 95%) ..... Time: 57.1 seconds, Memory: 66.6Mb There was 1 failure: 1) This_Feature_Subfeature_Test::test_do_that_thing Failed asserting that true is false. D:homewordpresswp-contentpluginsozh-demoteststest-mockfs.php:48 FAILURES! Tests: 124, Assertions: 1337, Failures: 1.
  • 13. $ 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 4.4.1 by Sebastian Bergmann. Configuration read from D:homewordpresswp-contentpluginsozh-demophpunit.xml .....................F..................................... 61 / 124 ( 49%) ........................................................... 119 / 124 ( 95%) ..... Time: 57.1 seconds, Memory: 66.6Mb There was 1 failure: 1) This_Feature_Subfeature_Test::test_do_that_thing Failed asserting that true is false. D:homewordpresswp-contentpluginsozh-demoteststest-mockfs.php:48 FAILURES! Tests: 124, Assertions: 1337, Failures: 1. Avec Unit Test :-)
  • 14. Unit Test Des bouts de code simples pour vérifier que chaque partie élementaire de l’ensemble du code complexe fonctionne comme attendu
  • 15. Automatiser ! PHP 5.3 PHP 5.4 PHP 5.5 PHP 5.6 hhvm WP 4.0 WP 4.1 trunk WP 3.9 multisite = 1 mulsite = 0
  • 16. Automatiser ! PHP 5.3 PHP 5.4 PHP 5.5 PHP 5.6 hhvm WP 4.0 WP 4.1 trunk WP 3.9 multisite = 1 mulsite = 0 Easy Digital Download : 21 configurations 350 tests avec 1100 “assertions” = 23 000 vérifications simples à chaque commit ou pull request
  • 19. Elle est bonne ta Pull Request ?
  • 20. With unit tests and continuous testing, there is always an extra pair of eyes looking at your code, helping you find the bugs you didn't know existed. Find one bug with unit tests before a release is pushed out and you will never NOT have unit test again. - Pippin Williamson, Easy Digital Downloads & autres
  • 22. Conventions & Pictogrammes my_file.php my_file.php (résumé) $ bash run_script.sh function do_stuff( ) class Test_Do_Stuff{ } Bla bla zoom
  • 23. Ingrédients • Une CLI confortable Minimum vital: bash, wget, curl, mysqladmin • SVN • GIT Noob? ➜ « Efficient Git setup on Windows » http://ozh.in/vl • PHPUnit • WP-CLI
  • 24. $ wget https://phar.phpunit.de/phpunit.phar $ chmod +x phpunit.phar (si besoin) $ mv phpunit.phar /usr/local/bin/phpunit ou, dans .bash_profile : alias phpunit="php d:/php/phpunit.phar $ phpunit --version PHPUnit 4.4.1 by Sebastian Bergmann Installer PHPUnit
  • 26. ... # For wp-cli & phpunit tests export WP_TESTS_DIR=/d/planetozh/wp-unittest-suite/wordpress-tests-lib export WP_CORE_DIR=/d/planetozh/wp-unittest-suite/wordpress ... ~/.bash_profile
  • 28. $ wp scaffold plugin-tests ozh-demo Success: Created test files. Créer les fichiers de test
  • 29. $ wp scaffold plugin-tests ozh-demo Success: Created test files. Créer les fichiers de test
  • 30. language: php php: - 5.3 - 5.4 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
  • 31. #!/usr/bin/env bash install_wp() { # dans $WP_CORE_DIR wget https://wordpress.org/wordpress_version_XX.tar.gz wget https://raw.github.com/markoheijnen/wp-mysqli/master/db.php } install_test_suite() { # dans $WP_TESTS_DIR svn https://develop.svn.wordpress.org/trunk/tests/phpunit/includes/ wget https://develop.svn.wordpress.org/trunk/wp-tests-config-sample.php } install_db() { mysqladmin create $DB_NAME } install-wp-tests.sh
  • 32. $ bash bin/install-wp-tests.sh wordpress_test root '' localhost latest Installer la « WP Test Suite »
  • 33. $ bash bin/install-wp-tests.sh wordpress_test root '' localhost latest + install_wp + mkdir -p C:/Users/Ozh/AppData/Local/Temp/wordpress + '[' latest == latest ']' + local ARCHIVE_NAME=latest + wget -nv -O /tmp/wordpress.tar.gz https://wordpress.org/latest.tar.gz + /bin/wget --no-check-certificate -nv -O /tmp/wordpress.tar.gz https://wordpress.org/latest.tar.gz WARNING: cannot verify wordpress.org's certificate, issued by `/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2': Unable to locally verify the issuer's authority. WARNING: certificate common name `*.wordpress.org' doesn't match requested host name `wordpress.org'. 2015-01-02 21:46:40 URL:https://wordpress.org/latest.tar.gz [6183711/6183711] -> C:/Users/Ozh/AppData/Local/Temp/wordpress ..... ... et patati et patata pendant 3 minutes Installer la « WP Test Suite »
  • 34. $ bash bin/install-wp-tests.sh wordpress_test root '' localhost latest + install_wp + mkdir -p C:/Users/Ozh/AppData/Local/Temp/wordpress + '[' latest == latest ']' + local ARCHIVE_NAME=latest + wget -nv -O /tmp/wordpress.tar.gz https://wordpress.org/latest.tar.gz + /bin/wget --no-check-certificate -nv -O /tmp/wordpress.tar.gz https://wordpress.org/latest.tar.gz WARNING: cannot verify wordpress.org's certificate, issued by `/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2': Unable to locally verify the issuer's authority. WARNING: certificate common name `*.wordpress.org' doesn't match requested host name `wordpress.org'. 2015-01-02 21:46:40 URL:https://joomla.org/latest.tar.gz [6183711/6183711] -> C:/Users/Ozh/CrapData/Local/Temp/weardress ..... ... WP_CORE_DIR WP_TESTS_DIR Installer la « WP Test Suite »
  • 36. <?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__ ) . '/../ozh-demo.php'; } tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' ); require $_tests_dir . '/includes/bootstrap.php'; tests/bootstrap.php 1) phpunit.xml ➜ 2) bootstrap.php
  • 37. <?php class SampleTest extends WP_UnitTestCase { function testSample() { // replace this with some actual testing code $this->assertTrue( true ); } } tests/test-sample.php 1) phpunit.xml ➜ 2) bootstrap.php ➜ 3) test-*.php
  • 38. $ phpunit Test micro ? 1, 2 …. 1, 2…
  • 39. $ 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 4.4.1 by Sebastian Bergmann. Configuration read from D:homeozhwp-ozh-pluginswp-contentpluginsozh- demophpunit.xml . Time: 6.1 seconds, Memory: 16.75Mb OK (1 test, 1 assertion) Test micro ? 1, 2 …. 1, 2…
  • 40. $ 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 4.4.1 by Sebastian Bergmann. Configuration read from D:homeozhwp-ozh-pluginswp-contentpluginsozh- demophpunit.xml . Time: 6.1 seconds, Memory: 16.75Mb OK (1 test, 1 assertion) Test micro ? 1, 2 …. 1, 2…
  • 42. <?php class Particular_Feature_Test extends WP_UnitTestCase { function test_sub_feature() { } } tests/test-that-feature.php
  • 43. <?php class Particular_Feature_Test extends WP_UnitTestCase { function test_sub_feature() { // REUNIR LES INGREDIENTS : contexte et dépendances // CUISINER : déclencher les actions qu'on veut tester // GOUTER : vérifier que c'est aussi bon que prévu } } tests/test-that-feature.php
  • 44. <?php class Particular_Feature_Test extends WP_UnitTestCase { function test_sub_feature() { // REUNIR LES INGREDIENTS : contexte et dépendances $needed_var = 'something'; $class_to_test = new Class_To_Test(); // CUISINER : déclencher les actions qu'on veut tester // GOUTER : vérifier que c'est aussi bon que prévu } } tests/test-that-feature.php
  • 45. <?php class Particular_Feature_Test extends WP_UnitTestCase { function test_sub_feature() { // REUNIR LES INGREDIENTS : contexte et dépendances $needed_var = 'something'; $class_to_test = new Class_To_Test(); // CUISINER : déclencher les actions qu'on veut tester $stuff = $class_to_test->feature( $needed_var ); $thing = do_something_with( $stuff ); // GOUTER : vérifier que c'est aussi bon que prévu } } tests/test-that-feature.php
  • 46. <?php class Particular_Feature_Test extends WP_UnitTestCase { function test_sub_feature() { // REUNIR LES INGREDIENTS : contexte et dépendances $needed_var = 'something'; $class_to_test = new Class_To_Test(); // CUISINER : déclencher les actions qu'on veut tester $stuff = $class_to_test->feature( $needed_var ); $thing = do_something_with( $stuff ); // GOUTER : vérifier que c'est aussi bon que prévu $this->assertEquals( 'résultat attendu', $thing ); $this->assertEmpty( $class_to_test->some_var ); $this->assertArrayHasKey( $stuff ); } } tests/test-that-feature.php
  • 47. PHPUnit : assertions assertClassHasAttribute() assertClassHasStaticAttribute() assertContains() assertContainsOnly() assertContainsOnlyInstancesOf() assertCount() assertEmpty() assertEqualXMLStructure() assertEquals() assertFalse() assertFileEquals() assertFileExists() assertGreaterThan() assertGreaterThanOrEqual() assertInstanceOf() assertInternalType() assertJsonFileEqualsJsonFile() assertJsonStringEqualsJsonFile() assertJsonStringEqualsJsonString() assertLessThan() assertLessThanOrEqual() assertNull() assertObjectHasAttribute() assertRegExp() assertStringMatchesFormat() assertStringMatchesFormatFile() assertSame() assertStringEndsWith() assertStringEqualsFile() assertStringStartsWith() assertThat() assertTrue() assertXmlFileEqualsXmlFile() assertXmlStringEqualsXmlFile() assertXmlStringEqualsXmlString() Principe : assertQuelqueChose() et assertNotQuelqueChose() Exemple : assertEmpty() et assertNotEmpty() https://phpunit.de/manual/current/en/appendixes.assertions.html
  • 48. PHPUnit : setUp( ) & tearDown( ) class Some_Test extends PHPUnit_Framework_TestCase { function test_quelquechose() { ... } function test_autre_chose() { ... } }
  • 49. class Some_Test extends PHPUnit_Framework_TestCase { function setUp() { do_things(); } function tearDown() { undo_the_things(); } function test_quelquechose() { ... } function test_autre_chose() { ... } } PHPUnit : setUp( ) & tearDown( )
  • 50. class Some_Test extends PHPUnit_Framework_TestCase { public static function setUpBeforeClass() { connect_to_DB(); } function setUp() { do_things(); } function tearDown() { undo_the_things(); } function test_quelquechose() { ... } function test_autre_chose() { ... } } PHPUnit : setUp( ) & tearDown( )
  • 51. PHPUnit : debug tip // bootstrap.php : function ut_var_dump( $what ) { ob_start(); var_dump( $what ); $display = ob_get_contents(); ob_end_clean(); fwrite( STDERR, $display ); } // test : class My_Cool_Test extends WP_UnitTestCase { function test_something() { ... ut_var_dump( $wp_something ); ... } }
  • 54. <?php /* Plugin Name: Unit Testing Demo Plugin */ class Ozh_Demo_Plugin { function __construct() { } ⤷ function init_plugin() { } ⤷ function add_short_code( $atts ) { } ⤷ function add_meta_if_title( $post_id ) { } ⤷ function get_song_title(){ } ⤷ function update_meta() { } function activate() { } ⤷ function create_page() { } ⤷ function create_tables() { } } new Ozh_Demo_Plugin; register_activation_hook( __FILE__, array( 'Ozh_Demo_Plugin', 'activate' ) ); ozh-demo.php
  • 55. <?php /* Plugin Name: Unit Testing Demo Plugin Plugin URI: https://github.com/ozh/plugin-unit-test-demo Description: Demo plugin. For unit tests and the lulz Author: Ozh Version: 0.1 Author URI: http://ozh.org */ class Ozh_Demo_Plugin { public $post_id; public $song_title; /** * Class contructor */ function __construct() { add_action( 'init', array( $this, 'init_plugin' ) ); do_action( 'ozh_demo_plugin_loaded' ); } function init_plugin() { } __construct( )
  • 56. /** * Test the constructor function */ class Ozh_Demo_Construct_Test extends WP_UnitTestCase { public $demo_plugin; function setUp() { parent::setUp(); $this->demo_plugin = new Ozh_Demo_Plugin; } function test_construct() { $this->assertEquals( 10, has_action( 'init', array( $this->demo_plugin, 'init_plugin' ) ) ); $this->assertGreaterThan( 0, did_action( 'ozh_demo_plugin_loaded' ) ); } } Ozh_Demo_Construct_Test{ }
  • 57. /** * Test the constructor function */ class Ozh_Demo_Construct_Test extends WP_UnitTestCase { public $demo_plugin; function setUp() { parent::setUp(); $this->demo_plugin = new Ozh_Demo_Plugin; } function test_construct() { $this->assertEquals( 10, has_action( 'init', array( $this->demo_plugin, 'init_plugin' ) ) ); $this->assertGreaterThan( 0, did_action( 'ozh_demo_plugin_loaded' ) ); } } Ozh_Demo_Construct_Test{ }
  • 58. <?php class Ozh_Demo_Plugin { ... /** * Plugin init: starts all that's needed */ function init_plugin() { add_action( 'save_post', array( $this, 'add_meta_if_title' ) ); add_shortcode( 'omglol', array( $this, 'add_short_code' ) ); } function add_meta_if_title( $post_id ) { // do stuff } function add_short_code( $atts ) { return "OMG c’est trop LOL !"; } init_plugin( )
  • 59. <?php /** * Test the init function */ class Ozh_Demo_Init_Test extends WP_UnitTestCase { public $demo_plugin; function setUp() { } function test_init_plugin() { // Simulate WordPress init do_action( 'init' ); $this->assertEquals(10, has_action('save_post', array($this->demo_plugin, 'add_meta_if_title' ) ) ); global $shortcode_tags; $this->assertArrayHasKey( 'omglol', $shortcode_tags ); } } Ozh_Demo_Init_Test{ }
  • 60. <?php /** * Test the init function */ class Ozh_Demo_Init_Test extends WP_UnitTestCase { public $demo_plugin; function setUp() { } function test_init_plugin() { // Simulate WordPress init do_action( 'init' ); $this->assertEquals(10, has_action('save_post', array($this->demo_plugin, 'add_meta_if_title' ) ) ); global $shortcode_tags; $this->assertArrayHasKey( 'omglol', $shortcode_tags ); } } Ozh_Demo_Init_Test{ } Dans vos tests de plugin, ne testez pas les API WordPress ! Testez la manifestation la plus simple de la bonne utilisation de l’API.
  • 61. <?php class Ozh_Demo_Plugin { ... /** * Things to do when the plugin is activated */ function activate() { $this->create_tables(); $this->create_page(); } function create_tables() {...} function create_page() {...} } new Ozh_Demo_Plugin; register_activation_hook( __FILE__, array( 'Ozh_Demo_Plugin', 'activate' ) ); activate( )
  • 62. <?php /** * Test the activation function */ class Ozh_Demo_Activate_Test extends WP_UnitTestCase { function test_activate() { $demo_plugin = $this->getMockBuilder( 'Ozh_Demo_Plugin' ) ->setMethods( array( 'create_tables', 'create_page' ) ) ->getMock(); $demo_plugin->expects( $this->once() ) ->method( 'create_tables' ); $demo_plugin->expects( $this->once() ) ->method( 'create_page' ); $demo_plugin->activate(); } } Ozh_Demo_Activate_Test{ }
  • 63. <?php /** * Test the activation function */ class Ozh_Demo_Activate_Test extends WP_UnitTestCase { function test_activate() { $demo_plugin = $this->getMockBuilder( 'Ozh_Demo_Plugin' ) ->setMethods( array( 'create_tables', 'create_page' ) ) ->getMock(); $demo_plugin->expects( $this->once() ) ->method( 'create_tables' ); $demo_plugin->expects( $this->once() ) ->method( 'create_page' ); $demo_plugin->activate(); } } Ozh_Demo_Activate_Test{ }
  • 65. Les Mocks my_feature() dépendances Production my_feature() mocks Tests Mock: objets simulés qui reproduisent le comportement d'objets réels de manière contrôlée
  • 66. <?php if (class_exists( 'WP_Image_Editor' ) ) : class WP_Image_Editor_Mock extends WP_Image_Editor { ... public function resize( $max_w, $max_h, $crop = false ) { } public function multi_resize( $sizes ) { } ... } endif; WP Mocks : Mock Image Editor
  • 67. <?php class Plugin_With_Mail_Test extends WP_UnitTestCase { function setUp(){ parent::setUp(); $_SERVER['SERVER_NAME'] = 'example.com'; // IMPORTANT } function test_something_that_sends_mail() { ... $sent = wp_mail( 'ozh@ozh.org', 'OMG', 'WTF & KTHXBYE' ); $this->assertTrue( $sent ); ... } } WP Mocks : Mock Mailer
  • 68. WP_FileSystem : $wp_filesystem->mkdir( $plugin_path . '/config/' ); $wp_filesystem->put_contents( $filename, $content, FS_CHMOD_FILE); WordPress classes : ➜ WP_Filesystem_Direct ➜ WP_Filesystem_FTPext ➜ WP_Filesystem_ftpsocket ➜ WP_Filesystem_SSH2 http://codex.wordpress.org/Filesystem_API WordPress Tests classes : ➜ WP_Filesystem_MockFS https://develop.svn.wordpress.org/trunk/tests/phpunit/tests/filesystem/base.php WP Mocks : Mock FS
  • 69. class Ozh_Demo_Plugin { ... /** * Create a custom table */ function create_tables() { global $wpdb; $table_name = $wpdb->prefix . "ozh_demo"; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( `id` mediumint(9) NOT NULL AUTO_INCREMENT, `time` datetime DEFAULT '0000-00-00 00:00:00' NOT NULL, `name` tinytext NOT NULL, UNIQUE KEY id (id) ) $charset_collate;"; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); return dbDelta( $sql ); } ... } create_tables( )
  • 70. /** * Test the DB function */ class Ozh_Demo_DB_Test extends WP_UnitTestCase { function test_create_DB() { global $wpdb; $demo_plugin = new Ozh_Demo_Plugin; $created = $demo_plugin->create_tables(); // ut_var_dump( current( $created ) ); // string(30) "Created table wptests_ozh_demo" $this->assertStringStartsWith( "Created table", current( $created ) ); } } Ozh_Demo_Table_Test{ }
  • 71. class Ozh_Demo_Plugin { ... /** * Create a custom page */ function create_page() { global $user_ID; $page = array( 'post_type' => 'page', 'post_title' => 'Code is Poterie', 'post_content' => 'Opération Template du Dessert', 'post_status' => 'publish', 'post_author' => $user_ID, ); return wp_insert_post( $page ); } ... } create_page( )
  • 72. <?php /** * Test page creation */ class Ozh_Demo_Create_Page_Test extends WP_UnitTestCase { public $demo_plugin; public static function setUpBeforeClass() { global $wp_rewrite; $wp_rewrite->init(); $wp_rewrite->set_permalink_structure( '/%year%/%postid%/' ); $wp_rewrite->flush_rules(); } public static function tearDownAfterClass() { global $wp_rewrite; $wp_rewrite->init(); } function setUp(){ parent:setUp(); $this->demo_plugin = new Ozh_Demo_Plugin; } ... } Ozh_Demo_Page_Create_Test{ }
  • 73. <?php /** * Test page creation */ class Ozh_Demo_Create_Page_Test extends WP_UnitTestCase { ... function test_create_page_one_way() { $this->go_to( '/code-is-poterie/' ); $this->assertQueryTrue( 'is_404' ); $this->demo_plugin->create_page(); $this->go_to( '/code-is-poterie/' ); $this->assertQueryTrue( 'is_page', 'is_singular' ); } } Ozh_Demo_Page_Create_Test{ }
  • 74. class Ozh_Demo_Plugin { ... /** * Update post meta data 'with_song' with song title */ function update_meta() { update_post_meta( $this->post_id, 'with_song', $this->song_title ); } ... } update_meta( )
  • 75. <?php /** * Test the update meta function */ class Ozh_Demo_Update_Meta_Test extends WP_UnitTestCase { public $demo_plugin; function setUp() { } function test_update_meta() { $this->demo_plugin->post_id = ???; $this->demo_plugin->song_title = ???; $this->assertEmpty( get_post_meta( $post_id, 'with_song' ) ); $this->demo_plugin->update_meta(); $this->assertNotEmpty( get_post_meta( $post_id, 'with_song' ) ); } } Ozh_Demo_Update_Meta_Test{ }
  • 76. <?php /** * Test the update meta function */ class Ozh_Demo_Update_Meta_Test extends WP_UnitTestCase { public $demo_plugin; function setUp() { } function test_update_meta() { $this->demo_plugin->post_id = $this->factory->post->create(); $this->demo_plugin->song_title = rand_str( 32 ); $this->assertEmpty( get_post_meta( $post_id, 'with_song' ) ); $this->demo_plugin->update_meta(); $this->assertNotEmpty( get_post_meta( $post_id, 'with_song' ) ); } } Ozh_Demo_Update_Meta_Test{ }
  • 77. <?php // Create posts $page_id = $this->factory->post->create( array( 'post_title' => 'Test Page') ); $this->factory->post->create_many(5, array('post_date' => '2015-01-24 09:45:00')); // Create users $user = $this->factory->user->create(); // Comments, Terms, Categories, Tags, Attachments, Blogs, Networks $this->factory->comment->create(); $this->factory->term->create(); $this->factory->category>create() $this->factory->attachment->create_object('image.jpg', $post_id, $args); $this->factory->blog->create(); $this->factory->network->create(); // https://core.trac.wordpress.org/browser/trunk/tests/phpunit/includes/factory.php L’objet factory
  • 78. <?php /** * Test the update meta function */ class Ozh_Demo_Update_Meta_Test extends WP_UnitTestCase { public $demo_plugin; function setUp() { } function test_update_meta() { $this->demo_plugin->post_id = $this->factory->post->create(); $this->demo_plugin->song_title = rand_str( 32 ); $this->assertEmpty( get_post_meta( $post_id, 'with_song' ) ); $this->demo_plugin->update_meta(); $this->assertNotEmpty( get_post_meta( $post_id, 'with_song' ) ); } } Ozh_Demo_Update_Meta_Test{ }
  • 79. class Ozh_Demo_Plugin { ... /** * Get current song title played on that cool web radio */ function get_song_title(){ $remote = 'http://www.radiometal.com/player/song_infos_player.txt'; $body = wp_remote_retrieve_body( wp_remote_get( $remote ) ); /* sample return: <div id='playerartistname'>Metallica</div> <div id='playersongname'>Master of Puppets</div> */ preg_match_all( "!<div id='[^']*'>([^<]*)</div>!", $body, $matches ); // $matches[1][0]: artist - $matches[1][1]: song name if( $matches[1] ) { return $matches[1][0] . ' - ' . $matches[1][1]; } else { return false; } } } get_song_title( )
  • 80. <?php /** * Test the remote request functions */ class Ozh_Demo_Remote_Test extends WP_UnitTestCase { public $demo_plugin; function setUp(){ } function return_valid_html() { $html = "BLAH <div id='playerartistname'>Slayer</div> <div id='playersongname'>Raining Blood</div> BLAH"; return array( 'body' => $html ); } function test_remote_request_valid_HTML() { add_filter( 'pre_http_request', array( $this, 'return_valid_html' ) ); $this->assertSame( 'Slayer - Raining Blood', $this->demo_plugin->get_song_title() ); } } Ozh_Demo_Remote_Test{ }
  • 81. <?php /** * Test the remote request functions */ class Ozh_Demo_Remote_Test extends WP_UnitTestCase { function return_crap_html() { return array( 'body' => "Error 500 - come back later" ); } function return_no_html() { return array( ); } function test_remote_request_invalid_HTML() { add_filter( 'pre_http_request', array( $this, 'return_crap_html' ) ); $this->assertFalse( $this->demo_plugin->get_song_title() ); add_filter( 'pre_http_request', array( $this, 'return_no_html' ) ); $this->assertFalse( $this->demo_plugin->get_song_title() ); } } Ozh_Demo_Remote_Test{ }
  • 82. <?php /** * Test the remote request functions */ class Ozh_Demo_Remote_Test extends WP_UnitTestCase { function return_crap_html() { return array( 'body' => "Error 500 - come back later" ); } function return_no_html() { return array( ); } function return_valid_html() { } Ozh_Demo_Remote_Test{ }
  • 83. Trucs divers à retenir ou à faire
  • 84. function do_something( ) ➜ Janvier 2015 : 2 variables + 1 test = 1 valeur attendue ➜ Janvier 2018 : 6 variables + 3 tests + 5 fonctions internes + 2 librairies externes = la même valeur attendue ! Testez aussi le trivial !
  • 85. <?php require_once '/path/to/your-theme/functions.php'; function _manually_load_theme() { switch_theme( 'your-theme' ); } tests_add_filter( 'muplugins_loaded', '_manually_load_theme' ); Testez vos fonctions de thèmes !
  • 86. Testable ou dé-testable ? class Ozh_Demo_Plugin { function add_meta_if_title( ) { if ( 'DOING_AUTOSAVE' ) ... if ( ! current_user_can( ) ) ... $this->get_song_title(); if( $this->song_title ) { $this->update_meta(); } } function update_meta() { ... } function get_song_title(){ ... } }
  • 87. class Ozh_Demo_Plugin { function add_meta_if_title() { if ( 'DOING_AUTOSAVE' ) ... if ( ! current_user_can( ) ) ... $remote = ...; $body = wp_remote_retrieve_body( ); preg_match_all( ... ) ; $this->song_title = ...; if( $this->song_title ) { update_post_meta( ... ); } } } Testable ou dé-testable ? class Ozh_Demo_Plugin { function add_meta_if_title( ) { if ( 'DOING_AUTOSAVE' ) ... if ( ! current_user_can( ) ) ... $this->get_song_title(); if( $this->song_title ) { $this->update_meta(); } } function update_meta() { ... } function get_song_title(){ ... } }
  • 88. class Ozh_Demo_Plugin { function add_meta_if_title() { if ( 'DOING_AUTOSAVE' ) ... if ( ! current_user_can( ) ) ... $remote = ...; $body = wp_remote_retrieve_body( ); preg_match_all( ... ) ; $this->song_title = ...; if( $this->song_title ) { update_post_meta( ... ); } } } Testable = atomique class Ozh_Demo_Plugin { function add_meta_if_title( ) { if ( 'DOING_AUTOSAVE' ) ... if ( ! current_user_can( ) ) ... $this->get_song_title(); if( $this->song_title ) { $this->update_meta(); } } function update_meta() { ... } function get_song_title(){ ... } }
  • 89. # Dans .gitattributes # Exclude certain files or directories when generating an archive .travis.yml export-ignore composer.* export-ignore .git* export-ignore bin export-ignore tests export-ignore phpunit.xml export-ignore Pas de tests dans vos archive.zip ?
  • 90. # Dans .gitattributes # Exclude certain files or directories when generating an archive .travis.yml export-ignore composer.* export-ignore .git* export-ignore bin export-ignore tests export-ignore phpunit.xml export-ignore Pas de tests dans vos archive.zip ?
  • 91. 1) Bootstrap.php : require_once dirname( __FILE__ ) . '/My_Custom_Assertions.php'; Etendez l’objet WP_UnitTestCase
  • 92. 1) Bootstrap.php : require_once dirname( __FILE__ ) . '/My_Custom_Assertions.php'; 2) My_Custom_Assertions.php : class MyPlugin_UnitTestCase extends WP_UnitTestCase { /** * Assert something specific to my plugin */ function assertSpecificThing(...) { ... $this->assertFalse(...) } } Etendez l’objet WP_UnitTestCase
  • 93. 1) Bootstrap.php : require_once dirname( __FILE__ ) . '/My_Custom_Assertions.php'; 2) My_Custom_Assertions.php : class MyPlugin_UnitTestCase extends WP_UnitTestCase { /** * Assert something specific to my plugin */ function assertSpecificThing(...) { ... $this->assertFalse(...) } } 3) tests/test-something.php : class Something_Test extends MyPlugin_UnitTestCase { ... } Etendez l’objet WP_UnitTestCase
  • 94. 1) Bootstrap.php : require_once dirname( __FILE__ ) . '/My_Custom_Assertions.php'; 2) My_Custom_Assertions.php : class MyPlugin_UnitTestCase extends WP_UnitTestCase { /** * Assert something specific to my plugin */ function assertSpecificThing(...) { ... $this->assertFalse(...) } } 3) tests/test-something.php : class Something_Test extends MyPlugin_UnitTestCase { ... } Exemples : assertArchiveEquals(), assertArchiveContains(), assertArchiveFileCount() https://github.com/humanmade/backupwordpress/blob/master/tests/class-wp-test-hm-backup-testcase.php Etendez l’objet WP_UnitTestCase
  • 95. phpunit-coverage.xml <phpunit bootstrap="tests/bootstrap.php" ... > <filter> <blacklist> <directory>/home/planetozh/wp-unittest-suite</directory> <directory>./tests</directory> </blacklist> </filter> <logging> <log type="coverage-html" target="coverage" title="PHPUnit" charset="UTF-8" yui="true" highlight="true" lowUpperBound="50" highLowerBound="90"/> </logging> </phpunit> Générer le rapport de code coverage $ phpunit -c phpunit-coverage.xml Vérifiez votre Code Coverage
  • 96. phpunit-coverage.xml <phpunit bootstrap="tests/bootstrap.php" ... > <filter> <blacklist> <directory>/home/planetozh/wp-unittest-suite</directory> <directory>./tests</directory> </blacklist> </filter> <logging> <log type="coverage-html" target="coverage" title="PHPUnit" charset="UTF-8" yui="true" highlight="true" lowUpperBound="50" highLowerBound="90"/> </logging> </phpunit> Générer le rapport de code coverage $ phpunit -c phpunit-coverage.xml Vérifiez votre Code Coverage
  • 97. Selenium - http://www.seleniumhq.org/ <?php // Selenium - automates browsers. class ConnectTest extends PHPUnit_Extensions_Selenium2TestCase { protected function setUp() { $this->setBrowser("*chrome"); $this->setBrowserUrl("https://mylocal.dev/"); } public function testUserCanLogIn() { $this->open("/"); $this->click("css=img[alt="Twitter"]"); $this->waitForPageToLoad("30000"); $this->assertContains( ‘dashboard’, $this->title() ); } } Qunit – http://qunitjs.com/ <script> test("Array should contain four items", function () { equal( myArray.length, 4, "Pass! - array contains four items"); }); </script> Testez vos interfaces !
  • 98. Selenium - http://www.seleniumhq.org/ <?php // Selenium - automates browsers. class ConnectTest extends PHPUnit_Extensions_Selenium2TestCase { protected function setUp() { $this->setBrowser("*chrome"); $this->setBrowserUrl("https://mylocal.dev/"); } public function testUserCanLogIn() { $this->open("/"); $this->click("css=img[alt="Twitter"]"); $this->waitForPageToLoad("30000"); $this->assertContains( ‘dashboard’, $this->title() ); } } Qunit – http://qunitjs.com/ <script> test("Array should contain four items", function () { equal( myArray.length, 4, "Pass! - array contains four items"); }); </script> Testez vos interfaces !
  • 99. L’excellente documentation de PHPUnit ➜ https://phpunit.de/documentation.html Des tutoriaux supplémentaires ➜ http://voceplatforms.com/?s=unit+testing+wordpress ➜ https://pippinsplugins.com/tag/unit-tests/ ➜ http://planetozh.com/blog/ ?? Les Unit Tests de WordPress ➜ http://develop.svn.wordpress.org/trunk/ Des plugins avec tests ➜ https://github.com/easydigitaldownloads/Easy-Digital-Downloads ➜ https://github.com/humanmade/backupwordpress Et coder des tests ! ➜ Ecrire 10 tests la semaine prochaine ➜ Ecrire 100 tests le mois suivant ? ➜ 1 bug corrigé = 1 nouveau test Devoirs à la maison