Static class mocker

While making some refactoring here and there and increasing my tools to deal with Headway I’ve run into little to not testable code.
While Headway code itself has no issues, my code relying on its static methods might well have; adding to that my mania for a test-driven development approach only makes it worse.

What I’m testing

This is a class I’ve test-driven developed today

<?php
namespace tad\wrappers\headway;

use \tad\utils\Arr;

class BlockSettings
{
    protected $settings;
    protected $data;

    public function __construct($block, $className = null)
    {
        if (is_null($className)) {
            $className =  '\HeadwayBlocksData';
        }
        if (!class_exists($className)) {
            return null;
        }
        $block = $className::get_block($block);
        if (is_null($block) or !$block) {
            throw new \BadMethodCallException("$block is not a valid block", 1);
        }
        $this->settings = array();
        if (isset($block['settings'])) {
            $this->settings = $block['settings'];
            $this->data = Arr::camelBackKeys($this->settings);
        }
    }
    public function __get($key)
    {
        if (!Arr::isAssoc($this->settings)) {
            return null;
        }
        if (!array_key_exists($key, $this->settings) and !array_key_exists($key, $this->data)) {
            return null;
        }
        if (array_key_exists($key, $this->data)) {
            return $this->data[$key];
        }
        return null;
    }
    public function getSetting($key, $default = null)
    {
        if (!is_string($key)) {
            throw new \BadMethodCallException("Key must be a string", 1);
        }
        if (!is_null($this->$key)) {
            return $this->$key;
        }
        return $default;
    }
}

where, aside for a call to the trusted Arr utility class, I’m facing the problem to test a constructor method that uses a static class.
I’ve allowed for dependency injection in a broad sense in the constructor and resorted to a default value for the class name to avoid factory classes that would make wrapping, which I do for mere convenience and consistency, counter-productive.

How I’m testing it

Allowing dependency injection is just half of the work and the other side of it is based on a test helper class: the StaticMocker class.
I’ve not found that much literature about testing static methods save for a strong discouragement toward their implementation and usage and the suggestion to, at least, resort to adapter classes.
The dramatic thing about wrappers is that I tend to make them glorified adapters and using an adapter in an adapter seems redundant; furthermore I use static methods for mindless and stateless classes and Headway uses them a lot too.

<?php
use tad\wrappers\headway\BlockSettings as Settings;
use tad\test\helpers\StaticMocker;

class HeadwayBlockSettingsTest extends \tad\test\cases\TadLibTestCase
{
    protected $className = '\tad\test\helpers\StaticMocker';

    public function setUp()
    {
        StaticMocker::_reset();
    }
    public function testConstructWillThrowForInvalidBlockParameter()
    {
        StaticMocker::_addMethod('get_block', false);
        $this->setExpectedException('BadMethodCallException','', 1);
        $sut = new Settings('foo', $this->className);
    }
    public function testBlockSettingssAreExposedAsCamelBackProperties()
    {
        $val = array('settings' => array('foo' => 1, 'baz' => 2, 'bar' => 3));
        StaticMocker::_addMethod('get_block', $val);
        $sut = new Settings('someSetting', $this->className);
        $this->assertEquals(1, $sut->foo);
        $this->assertEquals(2, $sut->baz);
        $this->assertEquals(3, $sut->bar);
    }
    public function testConstructingOverBlockWithNoSettingsWillExposeNullProperties()
    {
        $val = array('foster' => array('foo' => 1, 'baz' => 2, 'bar' => 3));
        StaticMocker::_addMethod('get_block', $val);
        $sut = new Settings('someSetting', $this->className);
        $this->assertNull($sut->foo);
    }
    public function testSettingsAreAccessibleUsingTheGetSettingMethod()
    {
        $val = array('settings' => array('foo' => 1, 'baz' => 2, 'bar' => 3));
        StaticMocker::_addMethod('get_block', $val);
        $sut = new Settings('someSetting', $this->className);
        $this->assertEquals(1, $sut->getSetting('foo'));
        $this->assertEquals(2, $sut->getSetting('baz'));
        $this->assertEquals(3, $sut->getSetting('bar'));
    }
    public function testAccessingSettingsUsingGetSettingAllowsForADefaultValue()
    {
        $val = array('settings' => array('baz' => 2, 'bar' => 3));
        StaticMocker::_addMethod('get_block', $val);
        $sut = new Settings('someSetting', $this->className);
        $this->assertEquals('someValue', $sut->getSetting('foo', 'someValue'));
    }
    public function testDefaultReturnValueForMissingSettingsIsNull()
    {
        $val = array('settings' => array('baz' => 2, 'bar' => 3));
        StaticMocker::_addMethod('get_block', $val);
        $sut = new Settings('someSetting', $this->className);
        $this->assertNull($sut->getSetting('foo'));
    }
}

The simple usage

The StaticMocker will not mock static properties but will mock static methods like

// I want the 'someMethod' method to return 'someValue'
StaticMocker::_addMethod('someMethod', 'someValue');

and I can also set an optional listener to set expectations like

// I want to make sure that the subject under test will call
// the class 'someMethod' static method only once when its
// 'method' is called

// I want to know that someMethod has been called one time only
$listener = $this->getMock('stdClass', array('someMethod'));
$listener->expects($this->once())->method('someMethod');

// I want the static mocker to notice the listener if 'someMethod' gets called
StaticMocker::_setListener($listener);

// I want the 'someMethod' method to return 'someValue'
StaticMocker::_addMethod('someMethod', 'someValue');

// and then call the method on the subject under test
$sut->method();

The class itself

The code is on GitHub and the class is all in the below lines

<?php
namespace tad\test\helpers;

class StaticMocker
{
    protected static $methods;
    protected static $listener;

    public static function __callStatic($func, $args)
    {
        self::_maybeCallListener($func, $args);
        return self::$methods[$func];
    }
    public static function _addMethod($func, $returnValue = null)
    {
        self::$methods[$func] = $returnValue;
    }
    public static function _reset()
    {
        self::$methods = array();
        self::_setListener();
    }
    public static function _setListener($listener = null)
    {
        self::$listener = $listener;
    }
    public static function _getListener()
    {
        return self::$listener;
    }
    protected static function _maybeCallListener($func, $args)
    {
        if (!is_null(self::$listener) and method_exists(self::$listener, $func)) {
            call_user_method_array($func, self::$listener, $args);
        }
    }
}