Closures, Anonymous Classes and an alternative approach to Test Mocking (Part 2)

The last time I posted here, I was writing about Anonymous Functions and how they can be bound to any object (or class) to execute as though they are a method within the scope of that class (Closure Binding as an alternative to “use” variables); and in the first article in this series, I looked at using a Closure to access private and protected properties of an object.

I was going to write this particular article about using simple Anonymous Classes to create test doubles for Unit Testing – and may well return to that topic in a future article in the series – but Matt Brunt has written a good post on that topic already, so instead I’m going to focus on a different approach to using an Anonymous Class to verify the values of object properties that we otherwise couldn’t see directly when testing a class.

In Java-land, there is the concept of a Nested- or Inner-Class, an Anonymous Class that inherits the scope of the class in which it was created. PHP Anonymous Classes are slightly different, and can’t automatically inherit that scope; but it can be simulated – the PHP Docs even show an example of this – although it isn’t fully equivalent to the Java-land Nested-Class. We need to explicitly pass those properties from the Outer Class that we want to be accessible within the Anonymous Class when we create it, and we need to create it from within the Outer Class. Normally, that wouldn’t be an option in real-world coding: we don’t normally want to write our classes with a method to create a Nested-Class purely for test purposes, because anything that we write inside the class is a part of that class when it’s used in production as well. But we can take advantage of closure binding to add a method to that class only within our tests that can be used to build a Nested Class.

Why do we want to do this? Because it gives us the ability to test that internal properties of the class are set correctly when we test a method, without having to use other methods like getters to check those values. If we want to test that the constructor correctly sets injected values, then we don’t want to be dependent on the getter working correctly to test that they have been set, otherwise we’re not isolating our tests.

As an example, I have a set of classes for converting values between different units of measure, that needs testing:


class Distance {
    const METRES          = 'm';      //    metre (SI base unit)
    const KILOMETRES      = 'km';     //    1000 metres (SI unit)
    const MILES           = 'mi';     //    mile (International)
    const NAUTICAL_MILES  = 'nmi';    //    nautical mile (International)
    const YARDS           = 'yds';    //    yard (International)

    protected static $conversions = [
        self::METRES          => 1.0,
        self::KILOMETRES      => 1000.0,
        self::MILES           => 1609.344,
        self::NAUTICAL_MILES  => 1852.0,
        self::YARDS           => 0.9144,
    ];

    protected $distance = 0.0;

    function __construct($distance = NULL, $uom = self::METRES) {
        $this->setValue($distance, $uom);
    }

    public function setValue($distance = 0.0, $uom = self::METRES) {
        $this->distance = self::convertToMeters($distance, $uom);
    }

    public function getValue($uom = self::METRES) {
        return self::convertFromMeters($this->distance, $uom);
    }

    public static function convertToMeters($distance = 0.0, $uom = NULL) {
        $factor = self::$conversions[$uom];

        return $distance * $factor;
    }

    public static function convertFromMeters($distance = 0.0, $uom = NULL) {
        $factor = self::$conversions[$uom];

        return $distance / $factor;
    }
}

(code simplified for purposes of example)

As you can see, this class always stores the distance value internally in meters in a protected property so it cannot be accessed directly, converting a value to meters from whatever UoM it was originally in the constructor, and converting it to whatever UoM is requested in the getter. There is code logic in both the constructor and the getter methods that needs testing independently of each other; but traditional unit testing requires that I construct the object and use the getter to test the returned result, creating a problem for testing each in isolation. One argument would be that I should never have any code logic in the constructor, but it seems sensible for the purpose of this class to do so; or that I should make my $distance property public, but (until PHP allows read-only properties) that allows any code using this class to change that value directly, something that we rarely want. Another approach that could be advocated is to rewrite the code so that the distance is stored in its original UoM, and I store the units as well, eliminating code (other than validation) from the constructor, and only converting units in the getter. So there are alternative approaches, but all of them require changing the code that I already have to make it testable, and while there are sometimes real benefits in refactoring code in a way that does make it more testable, it shouldn’t be necessary to refactor code just so that it can be tested. Testing should be flexible enough to work with existing code.
If we could only see the value of the distance property after instantiation of the object, but without having to use the setter to access it, then this wouldn’t be a problem.

Enter SpyMaster


class SpyMaster {
    private $targetObject;

    public function __construct($targetObject) {
        $this->targetObject = $targetObject;
    }

    protected function getHandler() {
        return function() {
            $properties = [];
            foreach(array_keys(get_object_vars($this)) as $propertyName) {
                $properties[$propertyName] = &$this->$propertyName;
            }

            return new class ($properties) {
                public function __construct(&$properties) {
                    foreach($properties as $propertyName => &$propertyValue) {
                        $this->$propertyName = &$propertyValue;
                    }
                }
            };
        };
    }

    public function infiltrate() {
        $anonymous = $this->getHandler();
        return $anonymous->bindTo(
            $this->targetObject, get_class($this->targetObject)
        )();
    }
}

SpyMaster is a small block of code that I can use to created a Nested-Class, giving me visibility of all properties within the class that I’m testing. Using SpyMaster, I can create a “spy” object that I can then use to look at the distance property in my Distance object immediately after instantiation, to see that the constructor has set the correct value.

So I can instantiate the Distance object in my test, which sets the distance property in meters:


$distance = new Distance(10, Distance::MILES);

And then use SpyMaster to created an Anonymous Nested-Class that takes the scope of the Distance instance, and makes all of its internal properties visible to the SpyMaster.


$spy = (new SpyMaster($distance))->infiltrate();
var_dump($spy->distance);

And thus test that the distance property in the $distance object has been set to the correct value, without needing to call the getValue() method.


SpyMaster works by injecting/binding an Anonymous function (the function created in the getHandler() method) into the scope of the object being tested, and then invoking that function. The function itself creates an Anonymous Class as a Nested-Class; and because it’s bound to the scope of the object, it injects all the properties of that object into its own constructor, and sets them as public properties of the Anonymous Class. Because the Anonymous Function passes these “by reference”, and the constructor of the Anonymous Class sets its injected properties “by reference” as well, then these are all references to the properties of the class that is being tested, and any change to those properties is “seen” in the Anonymous Class.


The use of this SpyMaster technique isn’t restricted to testing class constructors, but can be used for testing any method that sets or changes object properties that aren’t otherwise accessible outside of the class being tested.

There’s another potential benefit of the way SpyMaster provides access to those properties, that might horrify some people. Because the property values are accessed “by reference” through the Anonymous spy instance, we can also use it to modify those values directly.

There are some situations where this is useful, such as creating a known state within the object that we are testing before calling the methods that we want to test; but for anybody that finds the idea truly abhorrent, then it’s quite simple to modify SpyMaster to store those “by reference” properties as protected or private within the spy, and provide a getter method to provide access the values without risk of updating them within our tests.


Hopefully this article has suggested a useful way of using Anonymous Classes within our tests without simply using them as doubles (although that is a perfectly useful approach for mocking), and a more detailed way of verifying the results of method calls than simply testing the returned value.

Advertisements
This entry was posted in PHP and tagged , , , , , . Bookmark the permalink.

One Response to Closures, Anonymous Classes and an alternative approach to Test Mocking (Part 2)

  1. Pingback: PHP Annotated Monthly – September 2017 | PhpStorm Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s