Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to Unit Testing WordPress Plugins

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Introduction to Unit Testing WordPress Plugins

The slides of a talk given at WordCamp San Diego 2018:

https://2018.sandiego.wordcamp.org/session/introduction-to-unit-testing-wordpress-plugins/

This presentation is an introduction to how to unit test WordPress plugins. PHPUnit is the standard PHP testing framework, but getting it working with a WordPress plugin is challenging. The presentation covers: – Advantages that unit tests provide – Scaffolding a new plugin and its unit tests using WP-CLI – Writing and running the first unit test.

Avatar for Behzod Saidov

Behzod Saidov

April 15, 2018
Tweet

More Decks by Behzod Saidov

Other Decks in Programming

Transcript

  1. Outline • What is unit testing? • Benefits of unit

    testing. • Tools / Setting up an environment to run tests. • Scaffolding a new plugin and its unit tests using WP-CLI. • Writing and running the first unit test. • Integration testing and where to go for learning more.
  2. Unit testing is a software testing method: • It verifies

    that individual units of code (mostly functions) work as expected. • In most cases, that means calling a function (or a method of an object) and checking the result. • The ultimate goal is to ensure that the behaviour of our function stays consistent.
  3. Better quality • You can test at much more granular

    level than point-and-click testing via a GUI. • See codebases of some well-known WordPress plugins (JetPack, Yoast SEO)
  4. Refactoring becomes easy • Tests give us some confidence that

    updates to the code won’t break any existing functionality. • We can refactor code and run our tests to ensure that it still works.
  5. Faster debugging • It becomes easier to isolate the cause

    of a bug by identifying where a test fails. • When bugs show up, tests can be written to ensure that these known issues do not reoccur again.
  6. Tests can provide documentation • Tests document expected uses of

    code. • Look at the relevant tests to see the expected outcomes of some functionality.
  7. Can be used to design your code • Define class,

    method names. Define expected return values of methods. • TDD - Test-driven development
  8. What do you need to start testing WP plugins? •

    UNIX-based operating system (Linux, macOS) • PHP • MySQL or MariaDB • PHPUnit testing library • WordPress test suite • WP-CLI (optional)
  9. Use VVV - Varying Vagrant Vagrants • Varying Vagrant Vagrants

    is an open source Vagrant configuration focused on WordPress development. • It’s the recommended local development environment for contributing to the WordPress core. https://make.wordpress.org/core/handbook/contribute/#local-development-ov erview • More information and installation instructions are on the official website: https://varyingvagrantvagrants.org
  10. • git • ngrep • Composer • phpMemcachedAdmin • phpMyAdmin

    • Opcache Status • Webgrind • NodeJs • grunt-cli • Mailcatcher • and more... VVV comes with • Ubuntu 14.04 LTS (Trusty Tahr) • WordPress Develop • WordPress Stable • WP-CLI • NGINX • MariaDB 10.1 • PHP • memcached • xdebug • PHPUnit • Subversion client
  11. 1. vagrant ssh 2. cd /srv/www/wordpress-default/ 3. wp scaffold plugin

    unit-tested-plugin-demo --plugin_name="Unit Tested Plugin Demo" 4. cd public_html/wp-content/plugins/unit-tested-plugin-demo/ 5. ls Commands to Run
  12. <?php /** * PHPUnit bootstrap file * * @package Unit_Tested_Plugin_Demo

    */ $_tests_dir = getenv( 'WP_TESTS_DIR' ); if ( ! $_tests_dir ) { $_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib'; } if ( ! file_exists( $_tests_dir . '/includes/functions.php' ) ) { echo "Could not find $_tests_dir/includes/functions.php, have you run bin/install-wp-tests.sh ?" . PHP_EOL; exit( 1 ); }
  13. // Give access to tests_add_filter() function. require_once $_tests_dir . '/includes/functions.php';

    /** * Manually load the plugin being tested. */ function _manually_load_plugin() { require dirname( dirname( __FILE__ ) ) . '/unit-tested-plugin-demo.php'; } tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' ); // Start up the WP testing environment. require $_tests_dir . '/includes/bootstrap.php';
  14. Usage: $ bash bin/install-wp-tests.sh <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]

    Example: $ bash bin/install-wp-tests.sh wordpress_test root '' localhost 4.9.5 Script: install-wp-tests.sh • Downloads the required WordPress version • Installs the WordPress test suite • Defines environment variables WP_TESTS_DIR and WP_CORE_DIR accordingly • Creates a database for tests • No need to run in VVV
  15. <?php /** * Class SampleTest * * @package Unit_Tested_Plugin_Demo */

    /** * Sample test case. */ class SampleTest extends WP_UnitTestCase { /** * A single example test. */ function test_sample() { // Replace this with some actual testing code. $this->assertTrue( true ); } }
  16. Class WP_UnitTestCase • Defined in wordpress-develop/public_html/tests/phpunit/includes/testcase.php • Extends PHPUnit_Framework_TestCase which

    is an alias for PHPUnit\Framework\TestCase • Includes utility functions and assertions useful for testing WordPress. • WordPress tests should inherit from this class. class SampleTest extends WP_UnitTestCase { /** * A single example test. */ function test_sample() { // Replace this with some actual testing code. $this->assertTrue( true ); } }
  17. What is a test? • The tests are public methods

    that are named with prefix “test”. • Alternatively, you can use the @test annotation in a method's docblock to mark it as a test method. class SampleTest extends WP_UnitTestCase { /** * A single example test. */ function test_sample() { // Replace this with some actual testing code. $this->assertTrue( true ); } /** * An example test with a docblock annotation. * @test */ function another_sample() { // Replace this with some actual testing code. $this->assertTrue( true ); } }
  18. Assertions • Assertion methods as assertTrue() are used to assert

    that an actual value matches an expected value. • PHPUnit comes with a lot of assertion methods. Some examples: assertFalse, assertEquals, assertNull, assertContains, assertStringStartsWith, assertStringEndsWith • Full list: https://phpunit.de/manual/current/en/appendixes.assertions.html // Some sample assertions. $stack = []; $this->assertEmpty( $stack ); array_push( $stack, 'item' ); $this->assertEquals( 1, count( $stack ) ); $this->assertContains( 'item', $stack );
  19. <?php namespace Unit_Tested_Plugin_Demo; class Calculator { function add( $num1, $num2

    ) { return $num1 + $num2; } function subtract( $num1, $num2 ) { return $num1 - $num2; } }
  20. <?php /** * Plugin Name: Unit Tested Plugin Demo *

    Plugin URI: PLUGIN SITE HERE * Description: PLUGIN DESCRIPTION HERE * Version: 0.1.0 * * @package Unit_Tested_Plugin_Demo */ // Load class files. require 'class-calculator.php';
  21. <?php use \Unit_Tested_Plugin_Demo\Calculator; class Test_Calculator extends WP_UnitTestCase { /** *

    Unit test for the add method * * @see Calculator::add(); */ function test_add() { $calculator = new Calculator(); $this->assertEquals( 4, $calculator->add( 2, 2 ) ); } }
  22. <?php use \Unit_Tested_Plugin_Demo\Calculator; class Test_Calculator extends WP_UnitTestCase { /** *

    Unit test for the add method * * @see Calculator::add(); */ function test_add() { $calculator = new Calculator(); $this->assertEquals( 5, $calculator->add( 2, 2 ) ); } }
  23. <?php use \Unit_Tested_Plugin_Demo\Calculator; class Test_Calculator extends WP_UnitTestCase { /** *

    Unit test for the add method * * @see Calculator::add(); */ function test_add() { $calculator = new Calculator(); $this->assertEquals( 4, $calculator->add( 2, 2 ) ); $this->assertEquals( 0, $calculator->add( -2, 2 ) ); $this->assertEquals( 4.4, $calculator->add( 2.2, 2.2 ) ); $this->assertEquals( 0.5, $calculator->add( 2, -1.5 ) ); } }
  24. Unit test for the subtract method is in the GitHub

    repo: https://github.com/behzod/unit-tested-plugin-demo
  25. <?php namespace Unit_Tested_Plugin_Demo; class Post_Updater { private $calculator; public function

    __construct( $calculator ) { $this->calculator = $calculator; } public function update( $post_id, $num1, $num2 ) { update_post_meta( $post_id, 'unit_tested_plugin_demo_meta', $this->calculator->add( $num1, $num2 ) ); } }
  26. <?php use \Unit_Tested_Plugin_Demo\Post_Updater; use \Unit_Tested_Plugin_Demo\Calculator; class Test_Post_Updater extends WP_UnitTestCase {

    /** * @test */ function it_updates_the_post_meta() { $post_updater = new Post_Updater( new Calculator() ); $post_id = $this->factory()->post->create(); $post_updater->update( $post_id, 2, 3 ); $this->assertEquals( 5, get_post_meta( $post_id, 'unit_tested_plugin_demo_meta', true ) ); } }
  27. Integration tests can: • write to a file • open

    a database connection • do something over the network
  28. Writing tests for the code is like physical exercise •

    It’s hard to get started • You need to motivate yourself • It can seem pointless first • In the long term, you will see obvious benefits
  29. Links / Read More • Sample code for this presentation:

    https://github.com/behzod/unit-tested-plugin-demo • Travis CI build for the sample code: https://travis-ci.org/behzod/unit-tested-plugin-demo • Read unit and integration tests of some well-known WordPress plugins ◦ JetPack: github.com/Automattic/jetpack/tree/master/tests ◦ Yoast SEO: github.com/Yoast/wordpress-seo/tree/trunk/tests ◦ AMP for WP: github.com/Automattic/amp-wp/tree/develop/tests • Foo Bar - Template plugin for scaffolding WordPress plugins by XWP: https://github.com/xwp/wp-foo-bar • Learn more about PHPUnit Assertions: https://phpunit.de/manual/current/en/appendixes.assertions.html • Learn about Mock Objects, Stub Methods and Dependency Injection: https://jtreminio.com/2013/03/unit-testing-tutorial-part-4-mock-objects-stub-methods-dep endency-injection/