Testing for Websites and Plugins

@jackbarker

Presented @ #WPMelb (October 2014)

To advance to the next slide use the arrow keys (or swipe left/right on touch devices).

Agenda

Testing : 101

A Test is:

What a test looks like:

In Selenium IDE (which we'll explore shortly), a test might look like this:

Command Target Value Explanation
AssertElementExists css=body #my-form input#firstname This test expects that an input with id "firstname" exists
AssertText css=body #my-form label.firstname Enter your first name: This test expects that a label with class "firstname" exists, and with text "Enter your first name:"

What a test looks like:

In PHPUnit, a test may look like this:

// Test that the sky's colour is blue
$testResult = assertEquals('blue', $sky->color);

Your website/product's automated test suite should:

Browser Automation with Selenium IDE

or, "Testing for Beginners"

Selenium IDE

(What IS it?)

Selenium IDE

(What does it DO?)

Getting started

Installing Selenium IDE

  1. Make sure you have Firefox installed.
  2. Grab Selenium IDE Firefox Plugin at: SeleniumHQ.org/download
  3. (Note - for our purposes, ignore "Selenium Server" and other products)
SeleniumHQ Downloads

Using Selenium

Worked Example for WhereTheTruck.at


Source Code available here: Selenium Test Files

Let's create a test for the following:

Test 1

Instruction: Select a city from the MapPicker

Expected behaviour: The map should be updated, to reflect the city you have selected.

Source code:Test 1

Test 1
Test 1

Saved Tests

Note: Tests are saved in an HTML format:

<!DOCTYPE html>
<html>
  <head profile="http://selenium-ide.openqa.org/profiles/test-case">
  <link rel="selenium.base" href="http://wherethetruck.at/" />
</head>
<body>
  <table><tbody>
    <!--Open homepage-->
    <tr>
      <td>open</td> <!-- Command -->
      <td>/</td>    <!-- Arg 1 -->
      <td></td>   <!-- Arg 2 -->
    </tr>
    <!--Click Mapselector, and select "Melbourne"-->
    <tr>
      <td>click</td>
      <td>id=mapselector-dd</td>
      <td></td>
    </tr>
    <tr>
      <td>clickAndWait</td>
      <td>link=Melbourne</td>
      <td></td>
    </tr>
    <!--Expected: "Melbourne" is selected-->
    <tr>
      <td>assertText</td>
      <td>css=#mapselector-dd &gt; span</td>
      <td>exact:City: Melbourne</td>
    </tr>
  </tbody></table>
</body></html>

Let's try a harder one:

Test 2

Instruction: Reload the page

Expected behaviour: The page should "remember" which city you were viewing earlier, and display the same city.

Source code:Test 2

Limitations of Selenium IDE

  1. Is your UI likely to undergo changes? If so, rework to your testing will likely be significant.
  2. How complex are the items you are seeking to test? In some cases you may be limited in what the tool allows you to do (e.g. Test that "on Tuesdays, a different banner is displayed")
  3. Some testing simply needs to be done manually.

Limitations of Selenium IDE

  1. Great for testing something that already works (but not a great option for TDD).
  2. Selenium's developers cite:

    "Selenium IDE is simply intended as a rapid prototyping tool...

    For serious, robust test automation use either Selenium 2 or Selenium 1."

    -- SeleniumHQ.org

  3. Whilst highly flexible, and easy for beginners, there are more precise tools (targetted at developers) that may provide a more reliable solution...
  4. Speaking of which... enter:

PHPUnit

The PHP Testing Framework

Installing PHPUnit

PHPUnit Basics

PHPUnit is started from the command line, using:

$ phpunit

Sample Output:

10 tests:

$ phpunit
PHPUnit 4.2.0 by Sebastian Bergmann.

..........

Time: 200 ms, Memory: 2.50Mb
OK (10 tests, 18 assertions)

2 failures:

$ phpunit
PHPUnit 4.2.0 by Sebastian Bergmann.

..F....F...

Time: 200 ms, Memory: 2.50Mb
FAILURES!
Tests: 10, Assertions: 18, Failures: 2.

Coding for PHPUnit

What do we get from PHPUnit?

Class: PHPUnit_Framework_TestCase

Example Use

PHPUnit_Framework_TestCase Assertions

What about WP_UnitTestCase?

What do I inherit from WP_UnitTestCase?

I will explain these items further in my next slides:

Object Factories

WP_UnitTestCase functions

The following functions are called immediatedly before (setUp) / after (tearDown) a test has been executed:

Setting up your development workspace

for PHPUnit and Wordpress Plugin Development

Recommended Directory structure
Directory Structure

Config files

phpunit.xml

This is the file that PHPUnit loads first.

<phpunit
      bootstrap="bootstrap.php"
      backupGlobals="false"
      colors="true"
      convertErrorsToExceptions="true"
      convertNoticesToExceptions="true"
      convertWarningsToExceptions="true">
      <testsuites>
          <testsuite>
              <directory prefix="test-" suffix=".php">./</directory>
          </testsuite>
      </testsuites>
  </phpunit>

Location:
[ www / myplugin-repo / myplugin-tests / phpunit.xml ]

Config files

bootstrap.php

This is the file that sets up the Wordpress Test Environment

<?php
    // The path to the WordPress tests checkout.
    define( 'WP_TESTS_DIR', '/srv/www/wordpress-develop/tests/phpunit/' );
    
// The path to the main file of the plugin to test. define( 'TEST_PLUGIN_FILE', '/srv/www/myplugin-repo/myplugin/myplugin.php' );
// Load the The WordPress suite require_once WP_TESTS_DIR . 'includes/functions.php';
// Manually load the plugin main file. function _manually_load_plugin() { require TEST_PLUGIN_FILE; } tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
// Commence our tests require WP_TESTS_DIR . 'includes/bootstrap.php';

Location:
[ www / myplugin-repo / myplugin-tests / bootstrap.php ]

Test Files

test-[whatever].php

Based on the configuration I've described, all PHP files in the myplugin-tests directory begining with "test-" shall be treated as tests for the purpose of exceuting phpunit from the command line.

<?php
    class MyPlugin_TestComponentOne extends WP_UnitTestCase {
      private $myplugin;
    
      public function setUp() {
        parent::setUp(); // Perform inherited setUp tasks.
        $this->myplugin = new MyPlugin; // Get an instance of our plugin.
      }
    
      public function myPluginTest1() {
        // insert test logic
        assertTrue( $myplugin->do_some_plugin_stuff() );
      }
    
    
      public function tearDown() {
        parent::tearDown(); // Perform any inherited tearDown tasks.
      }
    
    }

Location:
[ www / myplugin-repo / myplugin-tests / test-*.php ]

Tying it all together

Building your first plugin, using PHPUnit and TDD.

THIS SECTION IS COMING SOON!

Conclusion + "Gotchas"

Just some things to watch out for -

That's all

Thanks for stopping by :)