Adapter class usage example - testing code

In my previous post I’ve shown how I’d use the Functions adapter class in production and it’s now time to show what an adapter class was born from: to test code in isolation outside of a WordPress session.

The test goal

I will use the production code I used yesterday, the one below

// file 'MyTestableFilter.php'

<?php
use TAD\AdapterClasses\Functions;
class MyTestableFilter
{
    /**
     * An instance of the Functions adapter class
     * @var object
     */
    protected $functions = null;
    // hooks in on the filter
    public function __construct()
    {
        $this->functions = Functions::getInstance();
        // add the filter
        $this->functions->add_filter('img_caption_shortcode', array(
            $this,
            'imgCaptionShortcodeFilter'
        ) , 10, 3);
    }
    // this is the method that gets called each time the filter is applied
    public function imgCaptionShortcodeFilter($val, $attr, $content = null)
    {
        extract($this->functions->shortcode_atts(array(
            'id' => '',
            'align' => '',
            'width' => '',
            'caption' => ''
        ) , $attr));
        if (1 > (int)$width || empty($caption)) 
        return $val;
        $capid = '';
        if ($id) {
            $id = $this->functions->esc_attr($id);
            $capid = 'id="figcaption_' . $id . '" ';
            $id = 'id="' . $id . '" aria-labelledby="figcaption_' . $id . '" ';
        }

        return '<figure ' . $id . 'class="wp-caption ' . $this->functions->esc_attr($align) . '" style="width: ' . (10 + (int)$width)   . 'px">' . $this->functions->do_shortcode($content) . '<figcaption ' . $capid . 'class="wp-caption-text">' . $caption . '<  /figcaption></figure>';
    }
}   

Setup the testing

I will use PHPUnit for the unit-testing of the class and this means that the class will need to be decorated only once at the beginning of the tests using the ClassDecorator class.
PHPUnit allows two methods to run before and after all tests and I will use those two methods to decorate and undecorate the class.

<?php
use TAD\Helpers\ClassDecorator;
use TAD\AdapterClasses\Functions;
class MyTestableFilterTest extends \PHPUnit_Framework_TestCase
{
    /**
     * An instance of the ClassDecorator class
     * @var object
     */
    protected static $classDecorator = null;
    /**
     * The fully qualified name of the Functions adapter class
     * @var string
     */
    protected static $targetClass = '\TAD\AdapterClasses\Functions';
    /**
     * An mock instance of the Functions class
     * @var object
     */
    protected $mockFunctions = null;

    // Setup the environ

    /**
     * This method runs before any test is run
     */
    public static function setUpBeforeClass()
    {
        $filePath = dirname(__FILE__) . '/../../assets/functions.txt';
        self::$classDecorator = new ClassDecorator(self::$targetClass);
        self::$classDecorator->addMethodsFromFile($filePath);
    }
    /**
     * This method runs after all the tests have run
     */
    public static function tearDownAfterClass()
    {
        // remove the functions added to the Functions class
        self::$classDecorator->removeMethods();
    }
    /**
     * Runs before each test method
     */
    protected function setUp()
    {
        // get a mock instance of the Functions adapter class
        $this->mockFunctions = $this->getMock(self::$targetClass);
        // set this mock in place of the Functions instance
        Functions::setInstance($this->mockFunctions);
    }
    /**
     * Runs after each test method
     */
    protected function tearDown()
    {
        // unset the Functions instance (was a mock I injected)
        Functions::setInstance(null);
        // unset the mockFunctions variable
        $this->mockFunctions = null;
    }

    // Tests

    public function testAddsTheRightFilter()
    {
        // set the add_filter function to expect a call
        // to add a filter for 'img_caption_shortcode'
        // and return true because the add_filter function will in affirmative case
        $this->mockFunctions->expects($this->any())->method('add_filter')->with($this->equalTo('img_caption_shortcode'))->will($this->returnValue(true));
        // get an instance of MyTestableFilter
        // and this should trigger the invocation of the add_filter method
        $filter = new MyTestableFilter();
    }
}

In the over-commented code above I make use of the ClassDecorator to add, at test-time, the add_filter method, get a mock instance of the class (a stub actually, see the difference between mocks and stubs) and then set an expectation on it.
It goes without saying that the proper way to avoid all this phase every time is to extend (the inheritance sense) the \PHPUnit_Framework_TestCase and create a AdapterClassesTestCase class to extend in tests.

Test stars and co-stars

I laid out above the difficult part of setting up the test environment and a first test but further testing, expectations and mocking can go on.
For example, deciding all my tests would revolve around testing the output of the function with given parameters I could set the two less interesting methods esc_attr and do_shortcode to behave in a default way in any test

protected function setUp(){

    // get a mock instance of the Functions adapter class
    $this->mockFunctions = $this->getMock(self::$targetClass);

    // set esc_attr and do_shortcode to behave
    $this->mockFunctions->expects($this->any())->method('esc_attr')->will($this->returnValue('aligncenter'));
    $this->mockFunctions->expects($this->any())->method('do_shortcode')->will($this->returnValue('lorem ipsum dolor'));

    // set this mock in place of the Functions instance
    Functions::setInstance($this->mockFunctions);
}