Effizientere WordPress-Plugin-Entwicklung mit Softwaretests Martin Schütte
About DECK36 Small team of 7 engineers Longstanding expertise in designing, implementing and operating complex web systems Developing own data intelligence-focused tools and web services Offering our expert knowledge in Automation & Operation, Architecture & Engineering, Analytics & Data Logistics
1. Dev & Test Environments 2. Testing Variants Static Code Analysis Unit Testing Integration Testing Behaviour Testing 3. Integration & Automation
Main Questions How can I know (my) software is correct? How does my boss know software is correct? How do I know software implements a given design? How can we discuss what correct is, anyway? We always need: implicit assumptions, explicit specifications.
Levels of Testing. abstract Acceptance Tests Integration Tests Unit Tests specific
Example Plugin: Freifunkmeta
Use a Dev Environment
Vagrant Configuration tool for (VirtualBox) VM setup and provisioning. Local cloud Self service Instant provisioning Useful for development reproducible environment independent PHP 5.x setups try things and not jeopardise your dev environment
VagrantPress $ git clone https://github.com/chad-thompson/vagrantpress.git $ cd vagrantpress $ vagrant up will setup VM with: Ubuntu Precise (12.04), Apache 2.2, MySQL 5.5, PHP 5.3 Wordpress 3.8 phpmyadmin PHPUnit phpcs, phploc, phpdepend,
Testing Variants
Coding Style $ phpcs --standard=wordpress freifunkmeta.php FILE: [...]/plugins/freifunkmeta/freifunkmeta.php --------------------------------------------------------------------- FOUND 360 ERROR(S) AND 406 WARNING(S) AFFECTING 338 LINE(S) --------------------------------------------------------------------- 21 ERROR Incorrect indentation; expected 1 space, found 4 22 WARNING Line is indented with space not tab 33 ERROR String "Unable to retrieve URL %s, error: %s" does not require double quotes; use single quotes instead 322 ERROR Closing parenthesis of a multi-line function definition must be on a line by itself 440 ERROR Expected next thing to be a escaping function, not '"<option value='$city' $selected>$prettycity </option>"'
Code Metrics $ php pdepend_summary.php freifunkmeta.php Package/Class Method LoC %Comment CCN NPath ------------- ------ --- -------- --- ----- FF_Meta output_ff_contact 49 6.1 10 384 FF_Meta shortcode_handler 41 2.4 9 48 FF_Community construct 12 0.0 7 15625 FF_Meta register_stuff 18 0.0 5 16 FF_Meta aux_get_all_locations 23 8.7 5 6 FF_Community make_from_city 15 0.0 4 20 [...]
Example Plugin: Freifunkmeta WP Blog. WP Core FF_Meta Other Plugins FF_Dir FF_Community HTTP Get Service Output Formatter
Unit Testing WP Blog. WP Core FF_Meta Other Plugins FF_Dir FF_Community HTTP Get Service Output Formatter
Simple PHPUnit Test Case class LowLevelTests extends PHPUnit_Framework_TestCase { function setup() { $this->ffm = new FF_Meta(); } } function test_output_ff_state() { $data = array("state" => array("nodes" => 429)); $ret = $this->ffm->output_ff_state($data); $this->assertregexp('/429/', $ret); }
Unit Testing (contd.) WP Blog. WP Core FF_Meta Other Plugins FF_Dir FF_Community HTTP Get Service Output Formatter
Integration Testing WP Blog. WP Core FF_Meta Other Plugins FF_Dir FF_Community HTTP Get Service Output Formatter
Unit Testing with Mock Object WP Blog. WP Core FF_Meta Other Plugins FF_Dir FF_Community HTTP Get Service Output Formatter
Example: Test with Dependency Injection class MockDataService { function get($url) { return $some_fixed_data; } } class WpIntegrationTests extends WP_UnitTestCase { function setup() { parent::setup(); // get plugin instance and replace ext. data service: $this->plugin = $GLOBALS['wp-plugin-ffmeta']; $this->plugin->reset_external_data_service( new MockDataService() ); } //...
Example: Test with Dependency Injection //... function test_post_ff_services() { $post_attribs = array( 'post_title' => 'Test', 'post_content' => '[ff_services]' ); $post = $this->factory->post->create_and_get( $post_attribs ); // w/o filter: $this->assertequals($post_content, $post->post_content); } } // with filter: $output = apply_filters( 'the_content', $post->post_content ); $this->assertregexp('/radio\.ffhh/', $output);
PHPUnit Output
Behaviour Testing WP Blog. WP Core FF_Meta Other Plugins FF_Dir FF_Community HTTP Get Service Output Formatter
WordPress Shortcode Plugin Test. Feature: Use Shortcodes In order to use my Plugin As a website author I need to write posts with shortcodes Background: Given I am logged in as "admin" with "vagrant" Scenario: Without the plugin Given the plugin "freifunkmeta" is "inactive" When I write a post with title "test" and content "[ff_contact]" Then I should see "ff_contact" Scenario: With the plugin Given the plugin "freifunkmeta" is "active" When I write a post with title "test" and content "[ff_contact]" Then I should see "Twitter" in the ".ff_contact" element And I should not see "ff_contact"
Behat Output
Implementation / Translation A look behind the curtain: framework is clever but not magical some translation needed statements have to become executable code Mechanism: plain sentence method name quoted words arguments matching with annotated regular expressions methods yield success, exception, or pending exception
Example: Behat Context (PHP) /** * from MinkContext * Checks, that page contains specified text. * * @Then /^(?: I )should see "(?P<text>(?:[^"] \\")*)"$/ */ public function assertpagecontainstext($text) { $this->assertsession()->pagetextcontains( $this->fixstepargument($text)); }
The Big Picture Features. Step Definitions WebDriver Browser
The Big Picture Features. cucumber.js Behat (PHP) Cucumber (Ruby) Goutte PhantomJS Selenium Chrome Firefox
Unit & Behaviour Testing Unit Tests Behaviour Tests unit testing acceptance test scenarios programmers non-developers programming language language of business domain bottom-up top-down / outside-in assertxyz X should do Y tests derived from user stories execute user stories development tool design & communication tool
Automate!
Scripting Basis for all automation. Lots of useful builtins and packages: wp core download/install/config/ wp export/import wp plugin get/install/update/ wp scaffold _s/plugin/plugin-tests wp server
wp scaffold Generate skeleton code for a new plugin & unit tests: $ cd wordpress/wp-content/plugins $ wp scaffold plugin-tests awesome $ find awesome awesome/ awesome/awesome.php awesome/bin awesome/bin/install-wp-tests.sh awesome/tests awesome/tests/bootstrap.php awesome/tests/test-sample.php awesome/.travis.yml awesome/phpunit.xml
wp scaffold (contd.) Create WP instance and run unit tests: $ cd awesome $ bash./bin/install-wp-tests.sh wp_tests root vagrant latest... $ phpunit PHPUnit 4.0.17 by Sebastian Bergmann. [...] Configuration read from [...]/plugins/awesome/phpunit.xml. Time: 5.52 seconds, Memory: 23.50Mb OK (1 test, 1 assertion)
Version Control use version control! many possible workflows, e. g. branches for dev and release use pre-commit hooks, e. g. with php -l syntax check
Travis-CI Continuous Integration service for GitHub 1. gets notified on push 2. builds project 3. runs phpunit 4. summarizes results, alert on failure
Example.travis.yml language: php php: - 5.4-5.5 - hhvm env: - WP_VERSION=3.8.3 - WP_VERSION=latest before_script: - bash bin/install-wp-tests.sh wordpress_test \ root '' localhost $WP_VERSION script: phpunit
Travis-CI Pass
Automated Testing Target: no manual effort. Continuous Integration: frequent code check-ins verified by automated builds and tests quickly find bugs and regressions Continuous Deployment: (semi-)automated deployment plan for rollback
Costs and Benefits of Testing Testing (like Documentation) has a cost usually: productivity improvement > cost but find the right balance
Conclusion I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence. Kent Beck Links http://phpqatools.org/: PHP Quality Assurance Toolchain http://wpgear.org/: compendium of useful WP developer tools http://wptest.io/: test data for WP plugins and themes Ptah Dunbar: Automated Testing in WordPress, Really?! tuts+ articles by Tom McFarlin Conversation Is TDD Dead?
Thank You