automated testing in wordpress, really?!
DESCRIPTION
Did you know that WordPress has an automated test suite? It contains well over 1500 integration tests and growing. However one of the primary culprits of WordPress is in the quality of its plugins. Most plugins don't have an automated test suite you can run to verify all features are working as expected, and fail gracefully. In this talk, Ptah will introduce you to automated testing in WordPress using PHPUnit. We will cover concepts like unit testing, integration testing and end-to-end testing with examples in WordPress. You will leave the talk equipped with practical knowledge and ready to start adding an automated test suite to your plugins.TRANSCRIPT
Automated Testing in
WordPress,Really?!
Rate this talk: https://joind.in/10115
#dc4d - Automated Testing in WordPress with @ptahdunbar
#dc4d - Automated Testing in WordPress with @ptahdunbar
Ptah (Pirate) Dunbar
● Started with WordPress and PHP in ‘05
● Contributing developer toWordPress, BuddyPress, bbPress
● Full stack Web Developer
● Architect at LiveNinja.com
● WPMIA co-organizer and SoFloPHP member
☠ Became Pirate Dunbar
#dc4d - Automated Testing in WordPress with @ptahdunbar
Ptah (Pirate) Dunbar
● Started with WordPress and PHP in ‘05
● Contributing developer toWordPress, BuddyPress, bbPress
● Full stack Web Developer
● Architect at LiveNinja.com
● WPMIA co-organizer and SoFloPHP member
☠ Became Pirate Dunbar
#dc4d - Automated Testing in WordPress with @ptahdunbar
● Started with WordPress and PHP in ‘05
● Contributing developer toWordPress, BuddyPress, bbPress
● Full stack Web Developer
● Architect at LiveNinja.com
● WPMIA co-organizer and SoFloPHP member
☠ Became Pirate Dunbar
Ptah (Pirate) Dunbar
#dc4d - Automated Testing in WordPress with @ptahdunbar
#dc4d - Automated Testing in WordPress with @ptahdunbar
● Understand automated testing concepts,ideas and best practices.
● Learn PHPUnit basics and the WordPress testsuite.
● Resources and homework
Agenda
In one hour
#dc4d - Automated Testing in WordPress with @ptahdunbar
WordPress powers
1 in 5 websites
source: http://w3techs.com/blog/entry/wordpress_powers_1_in_5_websites
#dc4d - Automated Testing in WordPress with @ptahdunbar
WordPress community
28,5102,177
source: http://w3techs.com/blog/entry/wordpress_powers_1_in_5_websites
#dc4d - Automated Testing in WordPress with @ptahdunbar
“The result is that a lot of the plugins are written in poor code and turn out to be poorly compatible with other plugins”
— Yoast
http://yoast.com/plugin-future/
Fail.
#dc4d - Automated Testing in WordPress with @ptahdunbar
● WP_DEBUG
● var_dump();
● print_r();
● error_log();
● debug_backtrace();
Manual Testing
Pull out the tools
#dc4d - Automated Testing in WordPress with @ptahdunbar
● WP_DEBUG
● var_dump();
● print_r();
● error_log();
● debug_backtrace();
Manual Testing
Pull out the tools
Ad-hoc & Temporary
#dc4d - Automated Testing in WordPress with @ptahdunbar
● WP_DEBUG
● var_dump();
● print_r();
● error_log();
● debug_backtrace();
Manual Testing
SLOW & Error Prone
Pull out the tools
#dc4d - Automated Testing in WordPress with @ptahdunbar
● WP_DEBUG
● var_dump();
● print_r();
● error_log();
● debug_backtrace();
Pull out the tools
Manual Testing
Doesn’t scale
#dc4d - Automated Testing in WordPress with @ptahdunbar
#dc4d - Automated Testing in WordPress with @ptahdunbar
A scripted process that invokes your app to test
features and compares the outcome with expected
results.
Automated Testing
#dc4d - Automated Testing in WordPress with @ptahdunbar
Persistent var_dumps();
Automated Testing
#dc4d - Automated Testing in WordPress with @ptahdunbar
Better than checking the logs
Automated Testing
#dc4d - Automated Testing in WordPress with @ptahdunbar
The Bigger Picture
Automated Testing
Continuous Integration
Continuous Delivery
TDD
BDD
Agile
Scrum
Continuous InspectionReleasing early, releasing often
Phingvagrant
#dc4d - Automated Testing in WordPress with @ptahdunbar
Getting started
Automate Testing
#dc4d - Automated Testing in WordPress with @ptahdunbar
There are so many
Frameworks
CHOOSE YOUR FRAMEWORK
#dc4d - Automated Testing in WordPress with @ptahdunbar
CHOOSE YOUR FRAMEWORK
PHPUnithttp://phpunit.de/manual/
Sebastian Bergmann
#dc4d - Automated Testing in WordPress with @ptahdunbar
vim composer.json && composer update
{ "require-dev": {
"phpunit/phpunit": "3.7.*",
"phpunit/phpunit-selenium" : "*",
}
}
http://getcomposer.org
#dc4d - Automated Testing in WordPress with @ptahdunbar
$>./vendor/bin/phpunit
PHPUnit
#dc4d - Automated Testing in WordPress with @ptahdunbar
#dc4d - Automated Testing in WordPress with @ptahdunbar
PHPUnit
Terminology
#dc4d - Automated Testing in WordPress with @ptahdunbar
● Test CaseA set of conditions that you set up in order to assert expected outcome.
PHPUnit
Terminology
#dc4d - Automated Testing in WordPress with @ptahdunbar
PHPUnit
Terminology
● Test CaseA set of conditions that you set up in order to assert expected outcome.
● Test ClassA collection of test cases, extends PHPUnit
#dc4d - Automated Testing in WordPress with @ptahdunbar
PHPUnit
Terminology
● Test CaseA set of conditions that you set up in order to assert expected outcome.
● Test ClassA collection of test cases, extends PHPUnit
● Test SuiteA collection of test classes
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?php// test classclass CalTest extends PHPUnit_Framework_TestCase{
// test case public function testAddReturnsSumOfTwoPositiveIntegers() { // assert stuff. }}
PHPUnit
TEST CLASS
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?php// test classclass CalTest extends PHPUnit_Framework_TestCase{
// test case public function testAddReturnsSumOfTwoPositiveIntegers() { // assert stuff. }}
PHPUnit
TEST CLASS
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/loader.phpincludes/
admin.phpapi.php…
phpunit.xmltests/
adminTest.phpApiTest.php…
…
PHPUnit
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/loader.phpincludes/
admin.phpfunctions.php…
phpunit.xmltests/
integration/…
acceptance/…
…
PHPUnit
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?xml version="1.0" encoding="UTF-8"?><phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="tests"> <directory suffix="Test.php">tests/</directory> </testsuite> </testsuites></phpunit>
PHPUnit
phpunit.xml - configuration file for PHPUnit
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?xml version="1.0" encoding="UTF-8"?><phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="tests"> <directory suffix="Test.php">tests/</directory> </testsuite> </testsuites></phpunit>
PHPUnit
phpunit.xml
Configure your test suite location
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?xml version="1.0" encoding="UTF-8"?><phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="integration"> <directory suffix="Test.php">tests/integration</directory> </testsuite> <testsuite name="acceptance"> <directory suffix="Test.php">tests/acceptance</directory> </testsuite> </testsuites></phpunit>
PHPUnit
phpunit.xml
Configure your test suite location
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?xml version="1.0" encoding="UTF-8"?><phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="tests"> <directory suffix="Test.php">tests/</directory> </testsuite> </testsuites></phpunit>
PHPUnit
phpunit.xml
Bootstrap file is included before any tests run
#dc4d - Automated Testing in WordPress with @ptahdunbar
Assertions
PHPUnit
Explicitly check expected outcomeagaisnt actual outcome.
#dc4d - Automated Testing in WordPress with @ptahdunbar
Assertions
PHPUnit
Explicitly check expected outcomeagaisnt actual outcome.
$this->assertTrue(condition);
#dc4d - Automated Testing in WordPress with @ptahdunbar
Arrange, Act, Assert
PHPUnit
#dc4d - Automated Testing in WordPress with @ptahdunbar
function testThatItsTestingTime(){
}
1. A
2. A
3. A
PHPUnit
#dc4d - Automated Testing in WordPress with @ptahdunbar
function testThatItsTestingTime(){
}
1. A
2. A
3. Assert (check for the expected behavior)
PHPUnit
#dc4d - Automated Testing in WordPress with @ptahdunbar
function testThatItsTestingTime(){
}
1. A
2. Act (call the method/trigger the action)
3. Assert (check for the expected behavior)
PHPUnit
#dc4d - Automated Testing in WordPress with @ptahdunbar
function testThatItsTestingTime(){
}
1. Arrange (the context/dependencies)
2. Act (call the method/trigger the action)
3. Assert (check for the expected behavior)
PHPUnit
#dc4d - Automated Testing in WordPress with @ptahdunbar
Example
PHPUnit
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass CalTest extends PHPUnit_Framework_TestCase{ public function testAddReturnsSumOfTwoPositiveIntegers() { // Arrange
// Act
// Assert
}}
PHPUnit
plugin/tests/unit/calTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass CalTest extends PHPUnit_Framework_TestCase{ public function testAddReturnsSumOfTwoPositiveIntegers() { // Arrange
// Act
// Assert
}}
PHPUnit
plugin/tests/unit/calTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass CalTest extends PHPUnit_Framework_TestCase{ public function testAddReturnsSumOfTwoPositiveIntegers() { // Arrange
// Act
// Assert $this->assertEquals(3, $result);
}}
PHPUnit
plugin/tests/unit/calTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass CalTest extends PHPUnit_Framework_TestCase{ public function testAddReturnsSumOfTwoPositiveIntegers() { // Arrange
// Act $result = $calculator->add(1,2);
// Assert $this->assertEquals(3, $result);
}}
PHPUnit
plugin/tests/unit/calTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass CalTest extends PHPUnit_Framework_TestCase{ public function testAddReturnsSumOfTwoPositiveIntegers() { // Arrange
$calculator = new Calculator();
// Act $result = $calculator->add(1,2);
// Assert $this->assertEquals(3, $result);
}}
PHPUnit
plugin/tests/unit/calTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass CalTest extends PHPUnit_Framework_TestCase{ public function testAddReturnsSumOfTwoPositiveIntegers() { // Arrange
$calculator = new Calculator();
// Act $result = $calculator->add(1,2);
// Assert $this->assertEquals(3, $result);
}}
PHPUnit
plugin/tests/unit/calTest.php
./vendor/bin/phpunit
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass CalTest extends PHPUnit_Framework_TestCase{ public function testAddReturnsSumOfTwoPositiveIntegers() { // Arrange
$calculator = new Calculator();
// Act $result = $calculator->add(1,2);
// Assert $this->assertEquals(3, $result);
}}
PHPUnit
plugin/tests/unit/calTest.php
Time: 148ms, Memory: 2.75Mb
OK: (1 test, 1 assertions)
./vendor/bin/phpunit
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass CalTest extends PHPUnit_Framework_TestCase{ public function testAddReturnsSumOfTwoPositiveIntegers() { // Arrange
$calculator = new Calculator();
// Act $result = $calculator->add(1,2);
// Assert $this->assertEquals(3, $result);
}}
PHPUnit
plugin/tests/unit/calTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass CalTest extends PHPUnit_Framework_TestCase{ public function testAddReturnsSumOfTwoPositiveIntegers() { // Arrange
$calculator = new Calculator();
// Act $result = $calculator->add(2,2);
// Assert $this->assertEquals(3, $result);
}}
PHPUnit
plugin/tests/unit/calTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass CalTest extends PHPUnit_Framework_TestCase{ public function testAddReturnsSumOfTwoPositiveIntegers() { // Arrange
$calculator = new Calculator();
// Act $result = $calculator->add(2,2);
// Assert $this->assertEquals(3, $result);
}}
PHPUnit
plugin/tests/unit/calTest.php
./vendor/bin/phpunit
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass CalTest extends PHPUnit_Framework_TestCase{ public function testAddReturnsSumOfTwoPositiveIntegers() { // Arrange
$calculator = new Calculator();
// Act $result = $calculator->add(2,2);
// Assert $this->assertEquals(3, $result);
}}
PHPUnit
plugin/tests/unit/calTest.php
./vendor/bin/phpunit
Failed asserting that 4 equals 3
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange
// Act
// Assert
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange
// Act
// Assert$this->assertTrue($user instanceof ‘\LiveNinja\User\Entity’);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange
// Act$user = $service->persist($validUserdata);
// Assert$this->assertTrue($user instanceof ‘\LiveNinja\User\Entity’);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange$service = new \LiveNinja\User\Service;$validUserdata = [...];
// Act$user = $service->persist($validUserdata);
// Assert$this->assertTrue($user instanceof ‘\LiveNinja\User\Entity’);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange$service = new \LiveNinja\User\Service;$validUserdata = [...];
// Act$user = $service->persist($validUserdata);
// Assert$this->assertTrue($user instanceof ‘\LiveNinja\User\Entity’);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
./vendor/bin/phpunit
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange$service = new \LiveNinja\User\Service;$validUserdata = [...];
// Act$user = $service->persist($validUserdata);
// Assert$this->assertTrue($user instanceof ‘\LiveNinja\User\Entity’);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
./vendor/bin/phpunit
Time: 248ms, Memory: 1.95Mb
OK: (1 test, 1 assertions)
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange$service = new \LiveNinja\User\Service;$validUserdata = [...];
// Act$user = $service->persist($validUserdata);
// Assert$this->assertTrue($user instanceof ‘\LiveNinja\User\Entity’);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange$service = new \LiveNinja\User\Service;$validUserdata = [...];
// Act$user = $service->persist($validUserdata);
// Assert$this->assertTrue($user instanceof ‘\LiveRacoons\User\Entity’);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange$service = new \LiveNinja\User\Service;$validUserdata = [...];
// Act$user = $service->persist($validUserdata);
// Assert$this->assertTrue($user instanceof ‘\LiveRacoons\User\Entity’);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
./vendor/bin/phpunit
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange$service = new \LiveNinja\User\Service;$validUserdata = [...];
// Act$user = $service->persist($validUserdata);
// Assert$this->assertTrue($user instanceof ‘\LiveRacoons\User\Entity’);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
Failed asserting that false equals true
./vendor/bin/phpunit
#dc4d - Automated Testing in WordPress with @ptahdunbar
ASSERTIONSAppendix: http://phpunit.de/manual/3.7/en/appendixes.assertions.html
PHPUnit
Use the most specific assertion possible
● assertTrue();
● assertEquals();
● assertContains();
● assertGreaterThan();
● assertNotNull();
● assertFalse();
● assertNotEquals();
● assertContainsOnly();
● assertLessThan();
● assertSame();
#dc4d - Automated Testing in WordPress with @ptahdunbar
PHPUnit
There was 1 failure:
1) Tests_Basic::test_readmereadme.html's version needs to be updated to 3.9.Failed asserting that '3.8' matches expected '3.9'.
/private/tmp/wordpress-tests/tests/phpunit/tests/basic.php:29
FAIL
#dc4d - Automated Testing in WordPress with @ptahdunbar
PHPUnit
There was 1 failure:
1) Tests_User_Author::test_get_the_authorFailed asserting that two objects are equal.--- Expected+++ Actual@@ @@ WP_User Object ( 'data' => stdClass Object (- 'ID' => '3'- 'user_login' => 'User 1'- 'user_pass' => '$P$BpIqOzMNRGZNy9qxKL/3cCDCMe85o2.'- 'user_nicename' => 'user-1'- 'user_email' => '[email protected]'+ 'ID' => '2'+ 'user_login' => 'test_author'+ 'user_pass' => '$P$BUdMebxEjJ23.6LbH9ujvVUFBsUuZv/'+ 'user_nicename' => 'test_author'+ 'user_email' => '[email protected]' 'user_url' => '' 'user_registered' => '2013-12-20 15:31:01' 'user_activation_key' => '' 'user_status' => '0'- 'display_name' => 'User 1'+ 'display_name' => 'test_author' )- 'ID' => 3+ 'ID' => 2 'caps' => Array (- 'subscriber' => true+ 'author' => true ) 'cap_key' => 'wptests_capabilities' 'roles' => Array (- 0 => 'subscriber'+ 0 => 'author' ) 'allcaps' => Array ( 'read' => true 'level_0' => true- 'subscriber' => true+ 'upload_files' => true+ 'edit_posts' => true+ 'edit_published_posts' => true+ 'publish_posts' => true+ 'level_2' => true+ 'level_1' => true+ 'delete_posts' => true+ 'delete_published_posts' => true+ 'author' => true ) 'filter' => null )
/private/tmp/wordpress-tests/tests/phpunit/tests/user/author.php:50
FAIL
#dc4d - Automated Testing in WordPress with @ptahdunbar
PHPUnit
There was 1 failure:
1) Tests_User_Author::test_get_the_authorFailed asserting that two objects are equal.--- Expected+++ Actual@@ @@ WP_User Object ( 'data' => stdClass Object (- 'ID' => '3'- 'user_login' => 'User 1'- 'user_pass' => '$P$BpIqOzMNRGZNy9qxKL/3cCDCMe85o2.'- 'user_nicename' => 'user-1'- 'user_email' => '[email protected]'+ 'ID' => '2'+ 'user_login' => 'test_author'+ 'user_pass' => '$P$BUdMebxEjJ23.6LbH9ujvVUFBsUuZv/'+ 'user_nicename' => 'test_author'+ 'user_email' => '[email protected]' 'user_url' => '' 'user_registered' => '2013-12-20 15:31:01' 'user_activation_key' => '' 'user_status' => '0'- 'display_name' => 'User 1'+ 'display_name' => 'test_author' )- 'ID' => 3+ 'ID' => 2 'caps' => Array (- 'subscriber' => true+ 'author' => true ) 'cap_key' => 'wptests_capabilities' 'roles' => Array (- 0 => 'subscriber'+ 0 => 'author' ) 'allcaps' => Array ( 'read' => true 'level_0' => true- 'subscriber' => true+ 'upload_files' => true+ 'edit_posts' => true+ 'edit_published_posts' => true+ 'publish_posts' => true+ 'level_2' => true+ 'level_1' => true+ 'delete_posts' => true+ 'delete_published_posts' => true+ 'author' => true ) 'filter' => null )
/private/tmp/wordpress-tests/tests/phpunit/tests/user/author.php:50
FAIL
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange$service = new \LiveNinja\User\Service;$validUserdata = [...];
// Act$user = $service->persist($validUserdata);
// Assert$this->assertTrue($user instanceof ‘\LiveRacoon\User\Entity’);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange$service = new \LiveNinja\User\Service;$validUserdata = [...];
// Act$user = $service->persist($validUserdata);
// Assert$this->assertInstanceOf(‘\LiveNinja\User\Entity’, $user);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange$service = new \LiveNinja\User\Service;$validUserdata = [...];
// Act$user = $service->persist($validUserdata);
// Assert$this->assertInstanceOf(‘\LiveNinja\User\Entity’, $user);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
./vendor/bin/phpunit
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithValidUserdataReturnsUserObject() {
// Arrange$service = new \LiveNinja\User\Service;$validUserdata = [...];
// Act$user = $service->persist($validUserdata);
// Assert$this->assertInstanceOf(‘\LiveNinja\User\Entity’, $user);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
./vendor/bin/phpunit
Time: 148ms, Memory: 2.75Mb
OK: (1 test, 1 assertions)
#dc4d - Automated Testing in WordPress with @ptahdunbar
<?phpclass UserServiceTest extends PHPUnit_Framework_TestCase{ public function testPersistWithInvalidUserdataReturnsWPError() {
// Arrange$service = new \LiveNinja\User\Service;$invalidUserdata = [];
// Act$user = $service->persist($invalidUserdata);
// Assert$this->assertInstanceOf(‘WP_Error’, $user);
}
//…}
PHPUnit
plugin/tests/unit/LiveNinja/User/ServiceTest.php
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
WordPress with Testshttp://develop.svn.wordpress.org/trunk/
1858 Tests, 8611 Assertions, 2.59 minutes
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
#dc4d - Automated Testing in WordPress with @ptahdunbar
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
Getting started
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
WordPress Testsuite
<?xml version="1.0" encoding="UTF-8"?><phpunit bootstrap="tests/bootstrap-wp.php"> <testsuites> <testsuite name="tests"> <directory suffix="Test.php">tests/</directory> </testsuite> </testsuites></phpunit>
#dc4d - Automated Testing in WordPress with @ptahdunbar
WordPress Testsuite
<?xml version="1.0" encoding="UTF-8"?><phpunit bootstrap="tests/bootstrap-wp.php"> <testsuites> <testsuite name="tests"> <directory suffix="Test.php">tests/</directory> </testsuite>
<testsuite name="integration"> <directory suffix="Test.php">integration/</directory> </testsuite> </testsuites></phpunit>
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/PluginTest.php
<?phpclass PluginTest extends PHPUnit_Framework_TestCase{ // test cases...}
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/PluginTest.php
<?phpclass PluginTest extends WP_UnitTestCase{ // test cases...}
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
$>./vendor/bin/phpunit
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
#dc4d - Automated Testing in WordPress with @ptahdunbar
Run Testsinside of an isolated
WordPress Environment
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
ConfigureWordPressOptions
$GLOBALS['wp_tests_options'] = ['active_plugins' => [
'hello.php',...
],'current_theme' => 'kubrick',...
];
bootstrap.php
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
ConfigureWordPress
Includesfunction __muplugins_loaded(){
// code and stuff.require_once 'env-debug.php';
}tests_add_filter('muplugins_loaded', '__muplugins_loaded');
bootstrap.php
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
● Navigate to site URL (Updates globals)$this->get_url($url);
● Test WP_Query for Conditionals (is_page, is_single, is_404)$this->assertQueryTrue($arg1, $arg2, ...);
● Test for Errors$this->assertWPError($thing);
● Genereate WordPress data fixtures$this->factory->post->create_and_get();
$this->factory->comment->create_post_comments($pid, 100);
$this->factory->user->create_many(5);
$this->factory->blog->create();
and more…
WordPress Testsuite
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
// test casesfunction testRedirectForDateBasedPermalinks(){
// Arrange
// Act
// Assert
}}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
// test casesfunction testRedirectForDateBasedPermalinks(){
// Arrange
// Act
// Assert$this->assertQueryTrue( 'is_404' );
}}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
// test casesfunction testRedirectForDateBasedPermalinks(){
// Arrange$customWP = new WPCustomization;$this->factory->post->create(['post_date' => '2007-09-04 00:00:00']);
// Act$customWP->deprecate_unused_pages();$this->go_to('/2007/');
// Assert$this->assertQueryTrue( 'is_404' );
}}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
// test casesfunction testRedirectForDateBasedPermalinks(){
// Arrange$customWP = new WPCustomization;$this->factory->post->create(['post_date' => '2007-09-04 00:00:00']);
// Act$customWP->deprecate_unused_pages();$this->go_to('/2007/');
// Assert$this->assertQueryTrue( 'is_404' );
}}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
// test casesfunction testRedirectForDateBasedPermalinks(){
// Arrange$customWP = new WPCustomization;$this->factory->post->create(['post_date' => '2007-09-04 00:00:00']);
// Act$customWP->deprecate_unused_pages();$this->go_to('/2007/');
// Assert$this->assertQueryTrue( 'is_404' );
}}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
// test casesfunction testRedirectForDateBasedPermalinks(){
// Arrange$customWP = new WPCustomization;$this->factory->post->create(['post_date' => '2007-09-04 00:00:00']);
// Act$customWP->deprecate_unused_pages();$this->go_to('/2007/');
// Assert$this->assertQueryTrue( 'is_404' );
}}
./vendor/bin/phpunit
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
// test casesfunction testRedirectForDateBasedPermalinks(){
// Arrange$customWP = new WPCustomization;$this->factory->post->create(['post_date' => '2007-09-04 00:00:00']);
// Act$customWP->deprecate_unused_pages();$this->go_to('/2007/');
// Assert$this->assertQueryTrue( 'is_404' );
}}
./vendor/bin/phpunit
Time: 148ms, Memory: 2.75Mb
OK: (1 test, 1 assertions)
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
/** * @dataProvider getRequiredPlugins */function testAllRequiredPluginsAreActive($plugin){
// Assert$this->assertTrue( is_plugin_active($plugin),
sprintf('%s is not activated.', $plugin) );}
function getRequiredPlugins(){
return [[‘hello.php’],
];}
}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
/** * @dataProvider getRequiredPlugins */function testAllRequiredPluginsAreActive($plugin){
// Assert$this->assertTrue( is_plugin_active($plugin),
sprintf('%s is not activated.', $plugin) );}
function getRequiredPlugins(){
return [[‘hello.php’],
];}
}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
/** * @dataProvider getRequiredPlugins */function testAllRequiredPluginsAreActive($plugin){
// Assert$this->assertTrue( is_plugin_active($plugin),
sprintf('%s is not activated.', $plugin) );}
function getRequiredPlugins(){
return [[‘hello.php’],
];}
}
./vendor/bin/phpunit
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
/** * @dataProvider getRequiredPlugins */function testAllRequiredPluginsAreActive($plugin){
// Assert$this->assertTrue( is_plugin_active($plugin),
sprintf('%s is not activated.', $plugin) );}
function getRequiredPlugins(){
return [[‘hello.php’],
];}
}
./vendor/bin/phpunit
Time: 148ms, Memory: 2.75Mb
OK: (1 test, 1 assertions)
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
/** * @dataProvider getWPOptions */function testWPOptionSettingsAreConfigured($option_name, $option_value){
// Assert$this->assertSame($option_value, get_option($option_name));
}
function getWPOptions(){
return [[‘home’, ‘http://example.org/wp/’],[‘siteurl’, ‘http://example.org/’],
];}
}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
/** * @dataProvider getWPOptions */function testWPOptionSettingsAreConfigured($option_name, $option_value){
// Assert$this->assertSame($option_value, get_option($option_name));
}
function getWPOptions(){
return [[‘home’, ‘http://example.org/wp/’],[‘siteurl’, ‘http://example.org/’],
];}
}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
/** * @dataProvider getWPOptions */function testWPOptionSettingsAreConfigured($option_name, $option_value){
// Assert$this->assertSame($option_value, get_option($option_name));
}
function getWPOptions(){
return [[‘home’, ‘http://example.org/wp/’],[‘siteurl’, ‘http://example.org/’],
];}
}
./vendor/bin/phpunit
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/integration/WPCustomizationTest.php
WordPress Testsuite
<?phpclass WPCustomizationTest extends WP_UnitTestCase{
/** * @dataProvider getWPOptions */function testWPOptionSettingsAreConfigured($option_name, $option_value){
// Assert$this->assertSame($option_value, get_option($option_name));
}
function getWPOptions(){
return [[‘home’, ‘http://example.org/wp/’],[‘siteurl’, ‘http://example.org/’],
];}
}
./vendor/bin/phpunit
Time: 148ms, Memory: 2.75Mb
OK: (1 test, 1 assertions)
http://www.seleniumhq.org/
#dc4d - Automated Testing in WordPress with @ptahdunbar
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/acceptance/ConnectTest.php
Acceptance Testing
<?phpclass ConnectTest extends PHPUnit_Framework_TestCase{ protected function setUp() { }
public function testUserCanLogInViaTwitter() { }}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/acceptance/ConnectTest.php
Acceptance Testing
<?phpclass ConnectTest extends PHPUnit_Extensions_Selenium2TestCase{ protected function setUp() { }
public function testUserCanLogInViaTwitter() { }}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/acceptance/ConnectTest.php
Acceptance Testing
<?phpclass ConnectTest extends PHPUnit_Extensions_Selenium2TestCase{ protected function setUp() { $this->setBrowser("*chrome"); $this->setBrowserUrl("https://wpss.dev/"); }
public function testUserCanLogInViaTwitter() { }}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/acceptance/ConnectTest.php
Acceptance Testing
<?phpclass ConnectTest extends PHPUnit_Extensions_Selenium2TestCase{ protected function setUp() { $this->setBrowser("*chrome"); $this->setBrowserUrl("https://wpss.dev/"); }
public function testUserCanLogInViaTwitter() { $this->open("/");
}}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/acceptance/ConnectTest.php
Acceptance Testing
<?phpclass ConnectTest extends PHPUnit_Extensions_Selenium2TestCase{ protected function setUp() { $this->setBrowser("*chrome"); $this->setBrowserUrl("https://wpss.dev/"); }
public function testUserCanLogInViaTwitter() { $this->open("/"); $this->click("link=Log in"); $this->waitForPageToLoad("30000");
}}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/acceptance/ConnectTest.php
Acceptance Testing
<?phpclass ConnectTest extends PHPUnit_Extensions_Selenium2TestCase{ protected function setUp() { $this->setBrowser("*chrome"); $this->setBrowserUrl("https://wpss.dev/"); }
public function testUserCanLogInViaTwitter() { $this->open("/"); $this->click("link=Log in"); $this->waitForPageToLoad("30000"); $this->click("css=img[alt=\"Twitter\"]"); $this->waitForPageToLoad("30000");
}}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/acceptance/ConnectTest.php
Acceptance Testing
<?phpclass ConnectTest extends PHPUnit_Extensions_Selenium2TestCase{ protected function setUp() { $this->setBrowser("*chrome"); $this->setBrowserUrl("https://wpss.dev/"); }
public function testUserCanLogInViaTwitter() { $this->open("/"); $this->click("link=Log in"); $this->waitForPageToLoad("30000"); $this->click("css=img[alt=\"Twitter\"]"); $this->waitForPageToLoad("30000"); $this->assertContains( ‘dashboard’, $this->title() ); }}
#dc4d - Automated Testing in WordPress with @ptahdunbar
plugin/tests/acceptance/ConnectTest.php
Acceptance Testing
<?phpclass ConnectTest extends PHPUnit_Extensions_Selenium2TestCase{ protected function setUp() { $this->setBrowser("*chrome"); $this->setBrowserUrl("https://wpss.dev/"); }
public function testUserCanLogInViaTwitter() { $this->open("/"); $this->click("link=Log in"); $this->waitForPageToLoad("30000"); $this->click("css=img[alt=\"Twitter\"]"); $this->waitForPageToLoad("30000"); $this->assertContains( ‘dashboard’, $this->title() ); }}
Time: 148ms, Memory: 2.75Mb
OK: (1 test, 1 assertions)
#dc4d - Automated Testing in WordPress with @ptahdunbar
Acceptance
Selenium IDE Plugin
● Visually navigate throughout your site and generate a PHPUnittest case.
● Download Extension○ http://www.seleniumhq.
org/projects/ide/
● Download PHPUnit Formatter○ https://addons.mozilla.org/en-
US/firefox/addon/selenium-ide-php-formatters/
#dc4d - Automated Testing in WordPress with @ptahdunbar
Acceptance
Selenium IDE Plugin
● Visually navigate throughout your site and generate a PHPUnittest case.
● Download Extension○ http://www.seleniumhq.
org/projects/ide/
● Download PHPUnit Formatter○ https://addons.mozilla.org/en-
US/firefox/addon/selenium-ide-php-formatters/
#dc4d - Automated Testing in WordPress with @ptahdunbar
How can we be confident that our tests
cover everything?
#dc4d - Automated Testing in WordPress with @ptahdunbar
Testing boundaries
#dc4d - Automated Testing in WordPress with @ptahdunbar
● (User) Acceptance Testing
○ Verify that all features are done done.
○ Black-box testing, no knowledge of internals.
Testing boundaries
#dc4d - Automated Testing in WordPress with @ptahdunbar
● (User) Acceptance Testing
○ Verify that all features are done done.
○ Black-box testing, no knowledge of internals.
● Integration Testing
○ Test WordPress settings/configuration;
○ Compatibility between plugins and themes.
Testing boundaries
#dc4d - Automated Testing in WordPress with @ptahdunbar
● (User) Acceptance Testing
○ Verify that all features are done done.
○ Black-box testing, no knowledge of internals.
● Integration Testing
○ Test WordPress settings/configuration,
○ Compatibility between plugins and themes
● Unit Testing
○ Test class methods and functions in isolation, zero dependencies
○ Does one “behavoir”
Testing boundaries
#dc4d - Automated Testing in WordPress with @ptahdunbar
● (User) Acceptance Testing
Verify that all features are done done,
black-box testing, no knowledge of
internals
● Integration Testing
Test WordPress settings/configuration,
compatibility between plugins and
themes
● Unit Testing
Test class methods and functions in
isolation, zero dependencies,
does one “behavoir”.
Testing boundaries
AcceptanceTesting
Integration Testing
Unit Testing
#dc4d - Automated Testing in WordPress with @ptahdunbar
● (User) Acceptance Testing
Verify that all features are done done,
black-box testing, no knowledge of
internals
● Integration Testing
Test WordPress settings/configuration,
compatibility between plugins and
themes
● Unit Testing
Test class methods and functions in
isolation, zero dependencies,
does one “behavoir”.
Testing boundaries
AcceptanceTesting
Integration Testing
Unit Testing
#dc4d - Automated Testing in WordPress with @ptahdunbar
● (User) Acceptance Testing
Verify that all features are done done,
black-box testing, no knowledge of
internals
● Integration Testing
Test WordPress settings/configuration,
compatibility between plugins and
themes
● Unit Testing
Test class methods and functions in
isolation, zero dependencies,
does one “behavoir”.
Testing boundaries
Integration Testing
Unit Testing
AcceptanceTesting
#ATWP // Automated Testing in WordPress // @ptahdunbar
What to tests?
● Test plugin works in various WordPress setups
○ Does it work under multisite?
○ What about a custom content directory?
● Test all code paths in functions and methods
● Test compatiblity between most popular plugins
● Test that default pages exists
#ATWP // Automated Testing in WordPress // @ptahdunbar
What to tests?
● Test for theme support
● Test that post formats contain property elements
● Test any required assets that need to be loaded in
templates
● Test for required elements on a page
● Verify search results template displays search term
● Verify SEO meta tags
1. WordPress APIs
#ATWP // Automated Testing in WordPress // @ptahdunbar
What to not tests?
#ATWP // Automated Testing in WordPress // @ptahdunbar
What to not tests?
1. WordPress APIs
2. PHP language features
#ATWP // Automated Testing in WordPress // @ptahdunbar
What to not tests?
1. WordPress APIs
2. PHP language features
3. Third party vendor code
Getting into the groove
#ATWP // Automated Testing in WordPress // @ptahdunbar
#ATWP // Automated Testing in WordPress // @ptahdunbar
Getting into the groove
● Build out templates
● Build out templates○ Create HTML/CSS
#ATWP // Automated Testing in WordPress // @ptahdunbar
Getting into the groove
#ATWP // Automated Testing in WordPress // @ptahdunbar
Getting into the groove
● Build out templates○ Create HTML/CSS○ Identify dynamic elements and their data
structure
#ATWP // Automated Testing in WordPress // @ptahdunbar
Getting into the groove
● Build out templates○ Create HTML/CSS○ Identify dynamic elements and their data
structure○ Label them and fill them with dummy data
#ATWP // Automated Testing in WordPress // @ptahdunbar
Getting into the groove
○ Verbally state your trying to do
#ATWP // Automated Testing in WordPress // @ptahdunbar
Getting into the groove
○ Verbally state your trying to do
○ Verbally explain what the code does
#ATWP // Automated Testing in WordPress // @ptahdunbar
Getting into the groove
○ Verbally state your trying to do
○ Verbally explain what the code does
○ Do this alone or with a fellow dev :)
What’s Next?
#dc4d - Automated Testing in WordPress with @ptahdunbar
● Download WP Skeleton Family
○ https://github.com/ptahdunbar/wp-skeleton-site
○ https://github.com/ptahdunbar/wp-skeleton-plugin
○ https://github.com/ptahdunbar/wp-skeleton-theme
Get started
“A Walking Skeleton is a tiny implementation of the thinnest possible slice of real functionality that we can automatically
build, deploy and test end-to-end.”
#dc4d - Automated Testing in WordPress with @ptahdunbar
● Art of Unit Testing (.NET)
○ https://leanpub.com/u/royosherove
○ Udemy Five day course
● #GOOS Book (Java)
● XUnit Test Patterns (Java)
● Grumpy Books (PHP)
○ https://leanpub.com/u/chartjes
● Misko Hevery
Resources
#dc4d - Automated Testing in WordPress with @ptahdunbar
● Learn moar PHPUnit features
○ data providers,
○ mocks and stubs
○ wordpress testsuite
● Goal: Write at least 100 assertions!
Homework!
TODO
#dc4d - Automated Testing in WordPress with @ptahdunbar
Automated Testing
#dc4d - Automated Testing in WordPress with @ptahdunbar
Automated Testingincreases your productivity
#dc4d - Automated Testing in WordPress with @ptahdunbar
Automated Testingfacilitates more shipping
#dc4d - Automated Testing in WordPress with @ptahdunbar
Automated Testingscales with you
#dc4d - Automated Testing in WordPress with @ptahdunbar
Automated Testingis your professional duty
as a developer
#dc4d - Automated Testing in WordPress with @ptahdunbar
Thank youAutomated Testing in WordPress
Pirate Dunbar@[email protected]
Rate this talk:https://joind.in/10115